summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--ApiDocs.bp1
-rw-r--r--OWNERS36
-rw-r--r--ProtoLibraries.bp8
-rw-r--r--StubLibraries.bp2
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/Android.bp8
-rw-r--r--apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java24
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java40
-rw-r--r--apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto20
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java6
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java77
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java84
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/OWNERS1
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java54
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java83
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java42
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java67
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java28
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java28
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java54
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java957
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java143
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java110
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java144
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java100
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java389
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java90
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java469
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java135
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java130
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Modifier.java67
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/OWNERS5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java51
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java185
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING34
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java62
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java62
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java8
-rw-r--r--apex/media/framework/TEST_MAPPING2
-rw-r--r--apex/media/framework/java/android/media/Session2Command.java5
-rw-r--r--boot/Android.bp4
-rw-r--r--cmds/bootanimation/Android.bp2
-rw-r--r--cmds/bootanimation/BootAnimation.cpp268
-rw-r--r--cmds/bootanimation/BootAnimation.h10
-rw-r--r--cmds/screencap/screencap.cpp16
-rw-r--r--config/OWNERS8
-rw-r--r--core/api/current.txt50
-rw-r--r--core/api/module-lib-current.txt12
-rw-r--r--core/api/system-current.txt65
-rw-r--r--core/api/test-current.txt24
-rw-r--r--core/api/test-lint-baseline.txt14
-rw-r--r--core/java/Android.bp4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java123
-rw-r--r--core/java/android/accessibilityservice/AccessibilityTrace.java216
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl6
-rw-r--r--core/java/android/accounts/Account.java7
-rw-r--r--core/java/android/accounts/OWNERS7
-rw-r--r--core/java/android/app/ActivityTaskManager.java13
-rw-r--r--core/java/android/app/ActivityThread.java115
-rw-r--r--core/java/android/app/ActivityThreadInternal.java4
-rw-r--r--core/java/android/app/ApplicationPackageManager.java10
-rw-r--r--core/java/android/app/ConfigurationController.java38
-rw-r--r--core/java/android/app/ContextImpl.java11
-rw-r--r--core/java/android/app/GameManager.java16
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl14
-rw-r--r--core/java/android/app/IGameManagerService.aidl1
-rw-r--r--core/java/android/app/Notification.java10
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java252
-rw-r--r--core/java/android/app/ResourcesManager.java2
-rw-r--r--core/java/android/app/TaskInfo.java34
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/app/compat/PackageOverride.java17
-rw-r--r--core/java/android/app/usage/OWNERS2
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java8
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java82
-rw-r--r--core/java/android/bluetooth/BluetoothLeAudio.java1
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java11
-rw-r--r--core/java/android/bluetooth/BluetoothVolumeControl.java313
-rw-r--r--core/java/android/bluetooth/OobData.java7
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java26
-rw-r--r--core/java/android/bluetooth/le/ResultStorageDescriptor.java3
-rw-r--r--core/java/android/bluetooth/le/TruncatedFilter.java3
-rw-r--r--core/java/android/companion/Association.java4
-rw-r--r--core/java/android/content/ContentResolver.java2
-rw-r--r--core/java/android/content/Intent.java28
-rw-r--r--core/java/android/content/IntentFilter.java34
-rw-r--r--core/java/android/content/PermissionChecker.java4
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/PackageManager.java29
-rw-r--r--core/java/android/content/pm/PackageParser.java16
-rw-r--r--core/java/android/content/pm/PermissionInfo.java6
-rw-r--r--core/java/android/content/pm/Signature.java2
-rw-r--r--core/java/android/content/pm/SigningDetails.java928
-rw-r--r--core/java/android/content/pm/SigningInfo.java26
-rw-r--r--core/java/android/content/pm/parsing/ApkLite.java6
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java16
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java12
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java4
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java9
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageRead.java4
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java38
-rw-r--r--core/java/android/content/pm/parsing/result/ParseTypeImpl.java1
-rw-r--r--core/java/android/content/pm/permission/CompatibilityPermissionInfo.java57
-rw-r--r--core/java/android/hardware/SensorManager.java1
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java198
-rw-r--r--core/java/android/hardware/hdmi/OWNERS1
-rw-r--r--core/java/android/hardware/input/InputManager.java16
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java38
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java140
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java43
-rw-r--r--core/java/android/hardware/usb/UsbManager.java5
-rw-r--r--core/java/android/inputmethodservice/AbstractInputMethodService.java119
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java34
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java254
-rw-r--r--core/java/android/inputmethodservice/InputMethodServiceInternal.java86
-rw-r--r--core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java470
-rw-r--r--core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java378
-rw-r--r--core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java197
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java (renamed from core/java/com/android/internal/view/InputConnectionWrapper.java)279
-rw-r--r--core/java/android/net/NetworkIdentity.java32
-rw-r--r--core/java/android/net/NetworkTemplate.java15
-rw-r--r--core/java/android/net/VpnService.java2
-rw-r--r--core/java/android/net/vcn/OWNERS2
-rw-r--r--core/java/android/os/BatteryStatsManager.java77
-rw-r--r--core/java/android/os/BatteryUsageStats.java4
-rw-r--r--core/java/android/os/BinderProxy.java14
-rwxr-xr-xcore/java/android/os/Build.java13
-rw-r--r--core/java/android/os/Environment.java10
-rw-r--r--core/java/android/os/GraphicsEnvironment.java190
-rw-r--r--core/java/android/os/PersistableBundle.java2
-rw-r--r--core/java/android/os/SharedMemory.java13
-rw-r--r--core/java/android/os/StrictMode.java30
-rw-r--r--core/java/android/os/Vibrator.java29
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl12
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java2
-rw-r--r--core/java/android/provider/ContactsContract.java6
-rw-r--r--core/java/android/provider/DeviceConfig.java62
-rw-r--r--core/java/android/provider/Settings.java579
-rw-r--r--core/java/android/provider/Telephony.java11
-rw-r--r--core/java/android/service/autofill/BatchUpdates.java3
-rw-r--r--core/java/android/service/autofill/CharSequenceTransformation.java7
-rw-r--r--core/java/android/service/autofill/CompositeUserData.java5
-rw-r--r--core/java/android/service/autofill/CustomDescription.java5
-rw-r--r--core/java/android/service/autofill/Dataset.java39
-rw-r--r--core/java/android/service/autofill/DateTransformation.java7
-rw-r--r--core/java/android/service/autofill/DateValueSanitizer.java5
-rw-r--r--core/java/android/service/autofill/FieldClassification.java7
-rw-r--r--core/java/android/service/autofill/FillRequest.java2
-rw-r--r--core/java/android/service/autofill/FillResponse.java7
-rw-r--r--core/java/android/service/autofill/ImageTransformation.java9
-rw-r--r--core/java/android/service/autofill/NegationValidator.java4
-rw-r--r--core/java/android/service/autofill/RegexValidator.java7
-rw-r--r--core/java/android/service/autofill/SaveCallback.java4
-rw-r--r--core/java/android/service/autofill/SaveInfo.java3
-rw-r--r--core/java/android/service/autofill/SaveRequest.java5
-rw-r--r--core/java/android/service/autofill/TextValueSanitizer.java7
-rw-r--r--core/java/android/service/autofill/UserData.java9
-rw-r--r--core/java/android/service/autofill/augmented/FillController.java5
-rw-r--r--core/java/android/service/autofill/augmented/FillWindow.java8
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java12
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java11
-rw-r--r--core/java/android/service/notification/ScheduleCalendar.java59
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java35
-rw-r--r--core/java/android/service/textclassifier/TextClassifierService.java33
-rw-r--r--core/java/android/service/timezone/TimeZoneProviderService.java12
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java12
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java7
-rw-r--r--core/java/android/util/SparseArrayMap.java24
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java22
-rw-r--r--core/java/android/util/imetracing/OWNERS3
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java9
-rw-r--r--core/java/android/view/IWindowManager.aidl12
-rw-r--r--core/java/android/view/InputWindowHandle.java8
-rw-r--r--core/java/android/view/InsetsController.java2
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java2
-rw-r--r--core/java/android/view/MotionEvent.java30
-rw-r--r--core/java/android/view/RemoteAnimationAdapter.java22
-rw-r--r--core/java/android/view/SurfaceControl.java22
-rw-r--r--core/java/android/view/SurfaceView.java13
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java28
-rw-r--r--core/java/android/view/WindowManager.java84
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java202
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java65
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java26
-rw-r--r--core/java/android/view/autofill/AutofillManager.java95
-rw-r--r--core/java/android/view/autofill/AutofillRequestCallback.java72
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl8
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureCondition.java5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureContext.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java13
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java22
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java13
-rw-r--r--core/java/android/view/contentcapture/DataRemovalRequest.java3
-rw-r--r--core/java/android/view/contentcapture/DataShareRequest.java5
-rw-r--r--core/java/android/view/contentcapture/ViewNode.java14
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java118
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java15
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java14
-rw-r--r--core/java/android/view/textclassifier/TextLanguage.java10
-rw-r--r--core/java/android/webkit/WebViewLibraryLoader.java23
-rw-r--r--core/java/android/webkit/WebViewProvider.java11
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java23
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/android/widget/ImageView.java6
-rw-r--r--core/java/android/widget/ProgressBar.java34
-rw-r--r--core/java/android/widget/TextView.java19
-rw-r--r--core/java/android/window/ConfigurationHelper.java132
-rw-r--r--core/java/android/window/DisplayAreaInfo.java13
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java9
-rw-r--r--core/java/android/window/IRemoteTransitionFinishedCallback.aidl6
-rw-r--r--core/java/android/window/ITaskFragmentOrganizer.aidl52
-rw-r--r--core/java/android/window/ITaskFragmentOrganizerController.aidl33
-rw-r--r--core/java/android/window/IWindowOrganizerController.aidl4
-rw-r--r--core/java/android/window/TaskFragmentAppearedInfo.aidl (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt)14
-rw-r--r--core/java/android/window/TaskFragmentAppearedInfo.java86
-rw-r--r--core/java/android/window/TaskFragmentCreationParams.aidl23
-rw-r--r--core/java/android/window/TaskFragmentCreationParams.java182
-rw-r--r--core/java/android/window/TaskFragmentInfo.aidl23
-rw-r--r--core/java/android/window/TaskFragmentInfo.java170
-rw-r--r--core/java/android/window/TaskFragmentOrganizer.java170
-rw-r--r--core/java/android/window/TaskOrganizer.java1
-rw-r--r--core/java/android/window/TransitionFilter.java33
-rw-r--r--core/java/android/window/TransitionInfo.java170
-rw-r--r--core/java/android/window/WindowContainerTransaction.java349
-rw-r--r--core/java/android/window/WindowContext.java13
-rw-r--r--core/java/android/window/WindowContextController.java21
-rw-r--r--core/java/android/window/WindowProviderService.java39
-rw-r--r--core/java/android/window/WindowTokenClient.java97
-rw-r--r--core/java/com/android/internal/accessibility/util/AccessibilityUtils.java78
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl2
-rw-r--r--core/java/com/android/internal/content/om/OWNERS2
-rw-r--r--core/java/com/android/internal/inputmethod/EditableInputConnection.java (renamed from core/java/com/android/internal/widget/EditableInputConnection.java)58
-rw-r--r--core/java/com/android/internal/inputmethod/IInputContextInvoker.java532
-rw-r--r--core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl26
-rw-r--r--core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl35
-rw-r--r--core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl29
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracing.java (renamed from core/java/android/util/imetracing/ImeTracing.java)51
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java (renamed from core/java/android/util/imetracing/ImeTracingClientImpl.java)14
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java (renamed from core/java/android/util/imetracing/ImeTracingServerImpl.java)10
-rw-r--r--core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java (renamed from core/java/android/util/imetracing/InputConnectionHelper.java)63
-rw-r--r--core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java232
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java48
-rw-r--r--core/java/com/android/internal/os/Zygote.java2
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java273
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl8
-rw-r--r--core/java/com/android/internal/statusbar/RegisterStatusBarResult.java19
-rw-r--r--core/java/com/android/internal/util/OWNERS1
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java21
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl2
-rw-r--r--core/java/com/android/internal/view/InputBindResult.java14
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/android_hardware_input_InputApplicationHandle.h2
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp17
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.h5
-rw-r--r--core/jni/android_media_AudioSystem.cpp9
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp11
-rw-r--r--core/jni/android_view_InputEventSender.cpp1
-rw-r--r--core/jni/android_view_MotionEvent.cpp25
-rw-r--r--core/jni/android_view_SurfaceControl.cpp20
-rw-r--r--core/proto/android/server/OWNERS1
-rw-r--r--core/proto/android/server/accessibilitytrace.proto18
-rw-r--r--core/proto/android/server/inputmethod/OWNERS1
-rw-r--r--core/proto/android/server/windowmanagerservice.proto24
-rw-r--r--core/proto/android/view/OWNERS3
-rw-r--r--core/proto/android/view/inputmethod/OWNERS1
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/layout/notification_template_material_base.xml2
-rw-r--r--core/res/res/layout/notification_template_material_media.xml2
-rw-r--r--core/res/res/layout/notification_template_material_messaging.xml2
-rw-r--r--core/res/res/layout/notification_template_text.xml2
-rw-r--r--core/res/res/values-af/strings.xml1
-rw-r--r--core/res/res/values-am/strings.xml1
-rw-r--r--core/res/res/values-ar/strings.xml1
-rw-r--r--core/res/res/values-as/strings.xml1
-rw-r--r--core/res/res/values-az/strings.xml1
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml1
-rw-r--r--core/res/res/values-be/strings.xml1
-rw-r--r--core/res/res/values-bg/strings.xml1
-rw-r--r--core/res/res/values-bn/strings.xml1
-rw-r--r--core/res/res/values-bs/strings.xml1
-rw-r--r--core/res/res/values-ca/strings.xml1
-rw-r--r--core/res/res/values-cs/strings.xml1
-rw-r--r--core/res/res/values-da/strings.xml1
-rw-r--r--core/res/res/values-de/strings.xml1
-rw-r--r--core/res/res/values-el/strings.xml1
-rw-r--r--core/res/res/values-en-rAU/strings.xml1
-rw-r--r--core/res/res/values-en-rCA/strings.xml1
-rw-r--r--core/res/res/values-en-rGB/strings.xml1
-rw-r--r--core/res/res/values-en-rIN/strings.xml1
-rw-r--r--core/res/res/values-en-rXC/strings.xml1
-rw-r--r--core/res/res/values-es-rUS/strings.xml1
-rw-r--r--core/res/res/values-es/strings.xml1
-rw-r--r--core/res/res/values-et/strings.xml1
-rw-r--r--core/res/res/values-eu/strings.xml1
-rw-r--r--core/res/res/values-fa/strings.xml1
-rw-r--r--core/res/res/values-fi/strings.xml1
-rw-r--r--core/res/res/values-fr-rCA/strings.xml1
-rw-r--r--core/res/res/values-fr/strings.xml1
-rw-r--r--core/res/res/values-gl/strings.xml1
-rw-r--r--core/res/res/values-gu/strings.xml1
-rw-r--r--core/res/res/values-hi/strings.xml1
-rw-r--r--core/res/res/values-hr/strings.xml1
-rw-r--r--core/res/res/values-hu/strings.xml1
-rw-r--r--core/res/res/values-hy/strings.xml1
-rw-r--r--core/res/res/values-in/strings.xml1
-rw-r--r--core/res/res/values-is/strings.xml1
-rw-r--r--core/res/res/values-it/strings.xml1
-rw-r--r--core/res/res/values-iw/strings.xml1
-rw-r--r--core/res/res/values-ja/strings.xml1
-rw-r--r--core/res/res/values-ka/strings.xml1
-rw-r--r--core/res/res/values-kk/strings.xml1
-rw-r--r--core/res/res/values-km/strings.xml1
-rw-r--r--core/res/res/values-kn/strings.xml1
-rw-r--r--core/res/res/values-ko/strings.xml1
-rw-r--r--core/res/res/values-ky/strings.xml1
-rw-r--r--core/res/res/values-lo/strings.xml1
-rw-r--r--core/res/res/values-lt/strings.xml1
-rw-r--r--core/res/res/values-lv/strings.xml1
-rw-r--r--core/res/res/values-mk/strings.xml1
-rw-r--r--core/res/res/values-ml/strings.xml1
-rw-r--r--core/res/res/values-mn/strings.xml1
-rw-r--r--core/res/res/values-mr/strings.xml1
-rw-r--r--core/res/res/values-ms/strings.xml1
-rw-r--r--core/res/res/values-my/strings.xml1
-rw-r--r--core/res/res/values-nb/strings.xml1
-rw-r--r--core/res/res/values-ne/strings.xml1
-rw-r--r--core/res/res/values-nl/strings.xml1
-rw-r--r--core/res/res/values-or/strings.xml1
-rw-r--r--core/res/res/values-pa/strings.xml1
-rw-r--r--core/res/res/values-pl/strings.xml1
-rw-r--r--core/res/res/values-pt-rBR/strings.xml1
-rw-r--r--core/res/res/values-pt-rPT/strings.xml1
-rw-r--r--core/res/res/values-pt/strings.xml1
-rw-r--r--core/res/res/values-ro/strings.xml1
-rw-r--r--core/res/res/values-ru/strings.xml1
-rw-r--r--core/res/res/values-si/strings.xml1
-rw-r--r--core/res/res/values-sk/strings.xml1
-rw-r--r--core/res/res/values-sl/strings.xml1
-rw-r--r--core/res/res/values-sq/strings.xml1
-rw-r--r--core/res/res/values-sr/strings.xml1
-rw-r--r--core/res/res/values-sv/strings.xml1
-rw-r--r--core/res/res/values-sw/strings.xml1
-rw-r--r--core/res/res/values-ta/strings.xml1
-rw-r--r--core/res/res/values-te/strings.xml1
-rw-r--r--core/res/res/values-th/strings.xml1
-rw-r--r--core/res/res/values-tl/strings.xml1
-rw-r--r--core/res/res/values-tr/strings.xml1
-rw-r--r--core/res/res/values-uk/strings.xml1
-rw-r--r--core/res/res/values-ur/strings.xml1
-rw-r--r--core/res/res/values-uz/strings.xml1
-rw-r--r--core/res/res/values-vi/strings.xml1
-rw-r--r--core/res/res/values-zh-rCN/strings.xml1
-rw-r--r--core/res/res/values-zh-rHK/strings.xml1
-rw-r--r--core/res/res/values-zh-rTW/strings.xml1
-rw-r--r--core/res/res/values-zu/strings.xml1
-rw-r--r--core/res/res/values/attrs_manifest.xml3
-rw-r--r--core/res/res/values/config.xml43
-rw-r--r--core/res/res/values/public.xml150
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml17
-rw-r--r--core/res/res/xml/default_zen_mode_config.xml12
-rw-r--r--core/res/res/xml/sms_short_codes.xml3
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java126
-rw-r--r--core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java14
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java65
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java8
-rw-r--r--core/tests/coretests/src/android/content/pm/SigningDetailsTest.java19
-rw-r--r--core/tests/coretests/src/android/graphics/FontListParserTest.java48
-rw-r--r--core/tests/coretests/src/android/provider/DeviceConfigTest.java103
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java13
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java5
-rw-r--r--core/tests/coretests/src/android/widget/ProgressBarTest.java184
-rw-r--r--core/tests/coretests/src/android/window/WindowContextControllerTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java129
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java201
-rw-r--r--core/tests/coretests/src/com/android/internal/util/RingBufferTest.java178
-rw-r--r--core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java164
-rw-r--r--data/etc/car/Android.bp7
-rw-r--r--data/etc/car/com.android.car.activityresolver.xml1
-rw-r--r--data/etc/car/com.google.android.car.adaslocation.xml (renamed from packages/SystemUI/res/layout/global_actions_change_panel.xml)20
-rw-r--r--data/etc/privapp-permissions-platform.xml8
-rw-r--r--data/etc/services.core.protolog.json370
-rw-r--r--data/keyboards/Vendor_0171_Product_0419.kl55
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java153
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java176
-rw-r--r--errorprone/tests/res/android/app/AppOpsManager.java107
-rw-r--r--graphics/java/android/graphics/FontListParser.java39
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java41
-rw-r--r--keystore/java/android/security/GenerateRkpKey.java69
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java2
-rw-r--r--libs/WindowManager/Shell/res/layout/split_outline.xml26
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml18
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml20
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java131
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java131
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java151
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java127
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java76
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java169
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java359
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java497
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java75
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java71
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt63
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt45
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt44
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt56
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt58
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt111
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt80
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt62
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt48
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt39
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt60
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt52
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt35
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt28
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java28
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java21
-rw-r--r--libs/hwui/Properties.cpp21
-rw-r--r--libs/hwui/Properties.h14
-rw-r--r--libs/hwui/RecordingCanvas.cpp9
-rw-r--r--libs/hwui/SkiaCanvas.cpp39
-rw-r--r--libs/hwui/SkiaCanvas.h22
-rw-r--r--libs/hwui/VectorDrawable.cpp8
-rw-r--r--libs/hwui/VectorDrawable.h2
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp1
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.cpp2
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.h10
-rw-r--r--libs/hwui/hwui/Canvas.h4
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp1
-rw-r--r--libs/hwui/hwui/Paint.h17
-rw-r--r--libs/hwui/hwui/PaintFilter.h5
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp32
-rw-r--r--libs/hwui/jni/Paint.cpp3
-rw-r--r--libs/hwui/jni/PaintFilter.cpp4
-rw-r--r--libs/hwui/jni/Utils.cpp9
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp10
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp10
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp24
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp5
-rw-r--r--libs/hwui/renderthread/CanvasContext.h10
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp6
-rw-r--r--libs/hwui/utils/PaintUtils.h11
-rw-r--r--location/java/android/location/LocationManager.java1
-rw-r--r--media/Android.bp113
-rw-r--r--media/aidl/android/media/audio/common/AudioChannelMask.aidl76
-rw-r--r--media/aidl/android/media/audio/common/AudioConfig.aidl4
-rw-r--r--media/aidl/android/media/audio/common/AudioFormat.aidl1
-rw-r--r--media/aidl/android/media/audio/common/AudioOffloadInfo.aidl8
-rw-r--r--media/aidl/android/media/audio/common/AudioStreamType.aidl6
-rw-r--r--media/aidl/android/media/audio/common/AudioUsage.aidl6
-rw-r--r--media/aidl/android/media/permission/Identity.aidl1
-rw-r--r--media/aidl/android/media/soundtrigger/AudioCapabilities.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl)4
-rw-r--r--media/aidl/android/media/soundtrigger/ModelParameter.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger/ModelParameterRange.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl)4
-rw-r--r--media/aidl/android/media/soundtrigger/Phrase.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/Phrase.aidl)4
-rw-r--r--media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl)16
-rw-r--r--media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/Properties.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionConfig.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionEvent.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl)14
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionMode.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionStatus.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/SoundModel.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/SoundModelType.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl)10
-rw-r--r--media/aidl/android/media/soundtrigger/Status.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/Status.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl27
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl13
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl5
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl111
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl44
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl109
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl50
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl53
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl53
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl43
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl42
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl53
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl42
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl46
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl42
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl43
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl43
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl41
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl45
-rw-r--r--media/java/android/media/AudioAttributes.java12
-rw-r--r--media/java/android/media/AudioFormat.java89
-rw-r--r--media/java/android/media/AudioManager.java57
-rw-r--r--media/java/android/media/AudioSystem.java3
-rw-r--r--media/java/android/media/AudioTrack.java5
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl26
-rw-r--r--media/java/android/media/Image.java2
-rw-r--r--media/java/android/media/MediaCodec.java1
-rw-r--r--media/java/android/media/MediaCodecList.java4
-rw-r--r--media/java/android/media/MediaPlayer.java3
-rw-r--r--media/java/android/media/MediaRecorder.java4
-rw-r--r--media/java/android/media/MediaRouter.java8
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java2
-rw-r--r--media/java/android/media/audiopolicy/AudioMixingRule.java37
-rw-r--r--media/java/android/media/audiopolicy/AudioProductStrategy.java16
-rw-r--r--media/java/android/media/tv/TvView.java15
-rw-r--r--media/java/android/service/media/MediaBrowserService.java126
-rw-r--r--media/jni/android_media_MediaExtractor.cpp1
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java7
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java2
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java3
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java13
-rw-r--r--packages/CompanionDeviceManager/res/values-af/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-am/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ar/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-as/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-az/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bg/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bn/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-bs/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-cs/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-da/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-de/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-el/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-es-rUS/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-es/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-et/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fa/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-gu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hy/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-it/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-iw/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ka/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-kk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-km/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-kn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ko/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lo/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lt/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-lv/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ml/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-ms/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-my/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ne/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-or/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pa/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ru/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-si/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sq/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sv/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ta/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-tr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-uk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-uz/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-vi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zu/strings.xml8
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java51
-rw-r--r--packages/SettingsLib/Android.bp41
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-af/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-am/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ar/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-as/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-az/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-be/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-bg/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-bn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-bs/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ca/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-cs/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-da/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-de/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-el/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-es/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-et/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-eu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fa/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fi/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-gl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-gu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hi/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hy/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-in/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-is/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-it/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-iw/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ja/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ka/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-kk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-km/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-kn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ko/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ky/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-lo/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-lt/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-lv/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-mk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ml/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-mn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-mr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ms/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-my/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-nb/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ne/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-nl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-or/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pa/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pt/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ro/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ru/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-si/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sq/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sv/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sw/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ta/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-te/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-th/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-tl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-tr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-uk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ur/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-uz/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-vi/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zu/strings.xml21
-rw-r--r--packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java46
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java314
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java254
-rw-r--r--packages/SettingsProvider/res/values-mcc466/defaults.xml5
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml64
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java150
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java66
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java286
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java35
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java70
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java8
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/OWNERS2
-rw-r--r--packages/SystemUI/TEST_MAPPING8
-rw-r--r--packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml4
-rw-r--r--packages/SystemUI/res/drawable/ic_arrow_forward.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_friction_lock_closed.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_settings_24dp.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml34
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_background.xml23
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_footer_background.xml30
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml22
-rw-r--r--packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml33
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_off.xml31
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_on.xml31
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_selector.xml22
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_off_background.xml24
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_on_background.xml24
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_selector.xml22
-rw-r--r--packages/SystemUI/res/layout/global_actions_grid_lite.xml14
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml366
-rw-r--r--packages/SystemUI/res/layout/internet_list_item.xml99
-rw-r--r--packages/SystemUI/res/layout/qs_customize_panel_content.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml6
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml28
-rw-r--r--packages/SystemUI/res/layout/rotate_suggestion.xml25
-rw-r--r--packages/SystemUI/res/layout/split_shade_header.xml89
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml14
-rw-r--r--packages/SystemUI/res/values-am/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml14
-rw-r--r--packages/SystemUI/res/values-as/strings.xml14
-rw-r--r--packages/SystemUI/res/values-az/strings.xml14
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-be/strings.xml14
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml14
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml14
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml14
-rw-r--r--packages/SystemUI/res/values-da/strings.xml14
-rw-r--r--packages/SystemUI/res/values-de/strings.xml14
-rw-r--r--packages/SystemUI/res/values-el/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml14
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml14
-rw-r--r--packages/SystemUI/res/values-es/strings.xml14
-rw-r--r--packages/SystemUI/res/values-et/strings.xml14
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml14
-rw-r--r--packages/SystemUI/res/values-in/strings.xml14
-rw-r--r--packages/SystemUI/res/values-is/strings.xml14
-rw-r--r--packages/SystemUI/res/values-it/strings.xml14
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml14
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-km/strings.xml14
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml14
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml14
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml14
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml14
-rw-r--r--packages/SystemUI/res/values-my/strings.xml14
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml14
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-or/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml14
-rw-r--r--packages/SystemUI/res/values-si/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml14
-rw-r--r--packages/SystemUI/res/values-te/strings.xml14
-rw-r--r--packages/SystemUI/res/values-th/strings.xml14
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml14
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml14
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml14
-rw-r--r--packages/SystemUI/res/values/colors.xml14
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml46
-rw-r--r--packages/SystemUI/res/values/strings.xml36
-rw-r--r--packages/SystemUI/res/values/styles.xml39
-rw-r--r--packages/SystemUI/shared/lint-baseline.xml35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java76
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java55
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java75
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java99
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java73
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java287
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java225
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java553
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java851
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java345
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java103
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java198
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java186
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java157
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java111
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java102
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java269
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java246
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java)46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt257
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java371
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java13
-rw-r--r--packages/VpnDialogs/res/values-af/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-am/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ar/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-as/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-az/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-b+sr+Latn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-be/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-bg/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-bn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-bs/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ca/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-cs/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-da/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-de/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-el/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rAU/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rCA/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rGB/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rIN/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rXC/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-es-rUS/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-es/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-et/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-eu/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-fa/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-fi/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-fr-rCA/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-fr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-gl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-gu/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hi/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hu/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hy/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-in/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-is/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-it/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-iw/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ja/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ka/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-kk/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-km/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-kn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ko/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ky/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-lo/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-lt/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-lv/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-mk/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-ml/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-mn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-mr/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-ms/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-my/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-nb/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ne/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-nl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-or/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-pa/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-pl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-pt-rBR/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-pt-rPT/strings.xml7
-rw-r--r--packages/VpnDialogs/res/values-pt/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ro/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ru/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-si/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sk/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sq/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sv/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sw/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ta/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-te/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-th/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-tl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-tr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-uk/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ur/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-uz/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-vi/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zh-rCN/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zh-rHK/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zh-rTW/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zu/strings.xml1
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-af/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-am/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-as/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-az/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-be/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-da/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-de/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-el/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-es/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-et/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-in/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-is/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-it/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-km/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-my/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-or/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-si/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-te/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-th/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml21
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicBlend.java3
-rw-r--r--services/Android.bp4
-rw-r--r--services/OWNERS2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java328
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java327
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java286
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java53
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java66
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java194
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java81
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java30
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java55
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java72
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java46
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java18
-rw-r--r--services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java293
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java188
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java55
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java8
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java128
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java20
-rw-r--r--services/core/java/com/android/server/accounts/OWNERS10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java80
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java8
-rw-r--r--services/core/java/com/android/server/am/CacheOomRanker.java132
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java2
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java52
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java49
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java84
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java196
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java8
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java22
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java383
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java408
-rw-r--r--services/core/java/com/android/server/compat/overrides/OWNERS2
-rw-r--r--services/core/java/com/android/server/compat/overrides/TEST_MAPPING12
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java120
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java15
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java4
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java7
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java17
-rw-r--r--services/core/java/com/android/server/display/color/DisplayTransformManager.java4
-rw-r--r--services/core/java/com/android/server/hdmi/ActiveSourceHandler.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java31
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecKeycode.java109
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java33
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java15
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java16
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java8
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java31
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessage.java9
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java91
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java5
-rw-r--r--services/core/java/com/android/server/hdmi/RoutingControlAction.java88
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java12
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java19
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java67
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java1858
-rw-r--r--services/core/java/com/android/server/inputmethod/multi-client-ime.md194
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java102
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java4
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryDatabase.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryManager.java1
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java18
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java16
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java10
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java7
-rw-r--r--services/core/java/com/android/server/notification/VibratorHelper.java29
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java2
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java8
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java36
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java11
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java12
-rw-r--r--services/core/java/com/android/server/pm/KeySetManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java387
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java345
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java50
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageSignatures.java56
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java14
-rw-r--r--services/core/java/com/android/server/pm/Settings.java18
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java10
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java173
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING76
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java32
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java6
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/Permission.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java101
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java4
-rw-r--r--services/core/java/com/android/server/policy/DisplayFoldController.java27
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java18
-rw-r--r--services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java2
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java20
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemService.java6
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java15
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java55
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java84
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java21
-rw-r--r--services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java22
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java42
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java47
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java120
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java52
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java8
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java43
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java184
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java158
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java6
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/README.md154
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java456
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java305
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java168
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java (renamed from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java)90
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java340
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java257
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java232
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java27
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java64
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java44
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java39
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java288
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java389
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java21
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java23
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java28
-rw-r--r--services/core/java/com/android/server/timedetector/TEST_MAPPING20
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java11
-rw-r--r--services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java6
-rw-r--r--services/core/java/com/android/server/timezonedetector/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java46
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java31
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java69
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java32
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java39
-rw-r--r--services/core/java/com/android/server/utils/OWNERS4
-rw-r--r--services/core/java/com/android/server/vcn/OWNERS2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java52
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java554
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java531
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java88
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java89
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java105
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java204
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java4
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java134
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java681
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java71
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java11
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java3
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java2
-rw-r--r--services/core/java/com/android/server/wm/InputWindowHandleWrapper.java8
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java74
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java5
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java59
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java115
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java5
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java60
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java6
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java23
-rw-r--r--services/core/java/com/android/server/wm/Task.java2139
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java59
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2142
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java234
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java63
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java10
-rw-r--r--services/core/java/com/android/server/wm/Transition.java354
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java170
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java66
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java90
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java361
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java195
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java64
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java144
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java28
-rw-r--r--services/core/jni/Android.bp5
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp271
-rw-r--r--services/core/jni/gnss/Android.bp51
-rw-r--r--services/core/jni/gnss/GnssAntennaInfoCallback.cpp269
-rw-r--r--services/core/jni/gnss/GnssAntennaInfoCallback.h84
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.cpp3
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.h2
-rw-r--r--services/core/jni/gnss/Utils.cpp6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java12
-rw-r--r--services/java/com/android/server/SystemServer.java20
-rw-r--r--services/net/Android.bp1
-rw-r--r--services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java2
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml1
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java39
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp14
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml25
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java329
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java102
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java302
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java688
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java62
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java77
-rw-r--r--services/tests/servicestests/Android.bp2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java113
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java106
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java282
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java92
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java1054
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java1239
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java446
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/LedgerTest.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java40
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/Android.bp14
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml28
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java458
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java132
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java117
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java150
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java183
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java56
-rw-r--r--services/usage/OWNERS2
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java19
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java43
-rw-r--r--telecomm/TEST_MAPPING40
-rw-r--r--telecomm/java/android/telecom/Connection.java21
-rw-r--r--telecomm/java/android/telecom/InCallService.java6
-rw-r--r--telecomm/java/android/telecom/PhoneAccountHandle.java47
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java26
-rw-r--r--telephony/TEST_MAPPING40
-rw-r--r--telephony/java/android/telephony/ServiceState.java13
-rw-r--r--telephony/java/android/telephony/SmsManager.java6
-rw-r--r--telephony/java/android/telephony/TelephonyDisplayInfo.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java16
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl5
-rw-r--r--tests/BatteryStatsPerfTest/AndroidManifest.xml2
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java129
-rw-r--r--tests/BootImageProfileTest/OWNERS2
-rw-r--r--tests/FlickerTests/Android.bp22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt181
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt61
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt73
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt86
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt39
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt48
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt100
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt29
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt89
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt102
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt29
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt56
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt53
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt63
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt73
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml1
-rw-r--r--tests/SoundTriggerTestApp/res/layout/main.xml8
-rw-r--r--tests/SoundTriggerTestApp/res/values/strings.xml1
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java8
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java70
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java33
-rw-r--r--tests/UpdatableSystemFontTest/Android.bp20
-rw-r--r--tests/UpdatableSystemFontTest/AndroidTest.xml18
-rw-r--r--tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java13
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java174
-rw-r--r--tests/UpdatableSystemFontTest/testdata/Android.bp56
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java5
-rw-r--r--tests/vcn/OWNERS2
-rw-r--r--tools/aapt2/SdkConstants.cpp2
-rwxr-xr-xtools/aosp/aosp_sha.sh16
-rw-r--r--tools/codegen/src/com/android/codegen/Utils.kt4
-rw-r--r--tools/lint/Android.bp46
-rw-r--r--tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt360
-rw-r--r--tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt274
-rw-r--r--tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt789
1868 files changed, 57770 insertions, 19430 deletions
diff --git a/Android.bp b/Android.bp
index 157bdcbec6bb..6745ff158b24 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,6 +112,7 @@ filegroup {
":framework_native_aidl",
":gatekeeper_aidl",
":gsiservice_aidl",
+ ":guiconstants_aidl",
":idmap2_aidl",
":idmap2_core_aidl",
":incidentcompanion_aidl",
@@ -328,6 +329,8 @@ java_defaults {
],
sdk_version: "core_platform",
static_libs: [
+ // TODO(b/184162091)
+ "android.hardware.soundtrigger3-V1-java",
"bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 0aed5d9abea9..8cfc0247b8c4 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -149,6 +149,7 @@ droidstubs {
args: metalava_framework_docs_args +
" --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
write_sdk_values: true,
+ api_levels_sdk_type: "system",
}
/////////////////////////////////////////////////////////////////////
diff --git a/OWNERS b/OWNERS
index 4970dd122331..03cfac955858 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,23 +1,23 @@
# This top-level list should remain narrowly defined as team leads; individual
# teams are strongly encouraged to define narrower OWNERS files at deeper
# levels within the source tree; see OWNERS.md for more details
-akulian@google.com
-dsandler@android.com
-dsandler@google.com
-hackbod@android.com
-hackbod@google.com
-jjaggi@google.com
-jsharkey@android.com
-jsharkey@google.com
-lorenzo@google.com
-michaelwr@google.com
-nandana@google.com
-narayan@google.com
-ogunwale@google.com
-roosa@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
-yamasani@google.com
+akulian@google.com #{LAST_RESORT_SUGGESTION}
+dsandler@android.com #{LAST_RESORT_SUGGESTION}
+dsandler@google.com #{LAST_RESORT_SUGGESTION}
+hackbod@android.com #{LAST_RESORT_SUGGESTION}
+hackbod@google.com #{LAST_RESORT_SUGGESTION}
+jjaggi@google.com #{LAST_RESORT_SUGGESTION}
+jsharkey@android.com #{LAST_RESORT_SUGGESTION}
+jsharkey@google.com #{LAST_RESORT_SUGGESTION}
+lorenzo@google.com #{LAST_RESORT_SUGGESTION}
+michaelwr@google.com #{LAST_RESORT_SUGGESTION}
+nandana@google.com #{LAST_RESORT_SUGGESTION}
+narayan@google.com #{LAST_RESORT_SUGGESTION}
+ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+roosa@google.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@android.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@google.com #{LAST_RESORT_SUGGESTION}
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
@@ -31,3 +31,5 @@ per-file Android.mk = file:platform/build/soong:/OWNERS
per-file ApiDocs.bp = file:platform/build/soong:/OWNERS
per-file StubLibraries.bp = file:platform/build/soong:/OWNERS
per-file ProtoLibraries.bp = file:platform/build/soong:/OWNERS
+per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
+per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index 7e3cc2739cd0..db5ba2fd031f 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -98,7 +98,7 @@ java_library_host {
},
// Protos have lots of MissingOverride and similar.
errorprone: {
- javacflags: ["-XepDisableAllChecks"],
+ enabled: false,
},
}
@@ -124,6 +124,10 @@ java_library {
"libs/incident/proto/android/os/**/*.proto",
":service-permission-protos",
],
+ // Protos have lots of MissingOverride and similar.
+ errorprone: {
+ enabled: false,
+ },
}
// ==== java proto device library (for test only) ==============================
@@ -150,7 +154,7 @@ java_library {
sdk_version: "core_current",
// Protos have lots of MissingOverride and similar.
errorprone: {
- javacflags: ["-XepDisableAllChecks"],
+ enabled: false,
},
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 941a1fa033e4..44c55c26153d 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -410,7 +410,7 @@ java_library {
],
static_libs: [
"android-non-updatable.stubs.module_lib",
- "art.module.public.api.stubs",
+ "art.module.public.api.stubs.module_lib",
],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 85dd0c4be48d..766b8c455d23 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -23,15 +23,15 @@ package {
android_test_helper_app {
name: "LargeResourcesCompressed",
- static_libs: [ "androidx.appcompat_appcompat" ],
+ static_libs: ["androidx.appcompat_appcompat"],
}
genrule {
name: "LargeResourcesUncompressed",
- srcs: [ ":LargeResourcesCompressed" ],
+ srcs: [":LargeResourcesCompressed"],
out: ["LargeResourcesUncompressed.apk"],
- cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc"
- + " && zip $(out) resources.arsc"
+ cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc -d $(genDir)" +
+ " && zip -j $(out) $(genDir)/resources.arsc",
}
java_library {
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
index 21c4491fc371..971c0caced30 100644
--- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
@@ -24,8 +24,6 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertTrue;
-
import android.annotation.UiThread;
import android.app.Activity;
import android.content.ComponentName;
@@ -39,6 +37,7 @@ import android.perftests.utils.PerfManualStatusReporter;
import android.perftests.utils.TraceMarkParser;
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -53,6 +52,7 @@ import androidx.annotation.Nullable;
import androidx.test.filters.LargeTest;
import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
import junit.framework.Assert;
@@ -185,7 +185,6 @@ public class ImePerfTest extends ImePerfTestBase
public static class BaselineIme extends InputMethodService {
public static final int HEIGHT_DP = 100;
- private static int sPid;
@Override
public View onCreateInputView() {
@@ -196,14 +195,10 @@ public class ImePerfTest extends ImePerfTestBase
view.setPadding(0, 0, 0, 0);
view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height));
inner.setBackgroundColor(0xff01fe10); // green
- sPid = Process.myPid();
+ Log.v(TAG, "onCreateInputView");
return view;
}
- static int getPid() {
- return sPid;
- }
-
static ComponentName getName(Context context) {
return new ComponentName(context, BaselineIme.class);
}
@@ -281,9 +276,16 @@ public class ImePerfTest extends ImePerfTestBase
}
private void killBaselineIme() {
- assertTrue("PID of test and IME can't be same",
- Process.myPid() != BaselineIme.getPid());
- Process.killProcess(BaselineIme.getPid());
+ // pidof returns a space separated list of numeric PIDs.
+ String result = SystemUtil.runShellCommand(
+ "pidof com.android.perftests.inputmethod:BaselineIME");
+ for (String pid : result.trim().split(" ")) {
+ // The output may be empty if there is no process with the name.
+ if (TextUtils.isEmpty(pid)) {
+ continue;
+ }
+ Process.killProcess(Integer.parseInt(pid));
+ }
}
private void testShowOrHideImeWarm(final boolean show) throws Throwable {
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index b1c42a92af9c..42ef80c495b3 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -145,9 +145,11 @@ public class UserLifecycleTests {
@Test
public void createUser() {
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createUserNoFlags();
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -156,6 +158,7 @@ public class UserLifecycleTests {
@Test
public void createAndStartUser() throws RemoteException {
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
@@ -166,6 +169,7 @@ public class UserLifecycleTests {
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -181,12 +185,14 @@ public class UserLifecycleTests {
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -200,12 +206,14 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
// Waits for UserState.mUnlockProgress.finish().
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -217,11 +225,13 @@ public class UserLifecycleTests {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
switchUser(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
@@ -237,6 +247,7 @@ public class UserLifecycleTests {
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(testUser);
@@ -244,6 +255,7 @@ public class UserLifecycleTests {
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
@@ -257,11 +269,13 @@ public class UserLifecycleTests {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
switchUser(testUser);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
@@ -277,11 +291,13 @@ public class UserLifecycleTests {
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
stopUser(userId, false);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -295,12 +311,14 @@ public class UserLifecycleTests {
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerUserSwitchObserver(null, latch, userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(userId);
waitForLatch("Failed to achieve onLockedBootComplete for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
@@ -326,12 +344,14 @@ public class UserLifecycleTests {
}, new IntentFilter(Intent.ACTION_USER_STOPPED));
final CountDownLatch switchLatch = new CountDownLatch(1);
registerUserSwitchObserver(switchLatch, null, startUser);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(startUser);
waitForLatch("Failed to achieve ACTION_USER_STOPPED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
try {
switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
} catch (InterruptedException e) {
@@ -348,9 +368,11 @@ public class UserLifecycleTests {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createManagedProfile();
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
mRunner.resumeTiming();
@@ -365,11 +387,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -386,11 +410,13 @@ public class UserLifecycleTests {
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
startUserInBackgroundAndWaitForUnlock(userId);
stopUser(userId, true);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -408,12 +434,14 @@ public class UserLifecycleTests {
final int userId = createManagedProfile();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -438,12 +466,14 @@ public class UserLifecycleTests {
startApp(userId, DUMMY_PACKAGE_NAME);
stopUser(userId, true);
SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -457,11 +487,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -478,6 +510,7 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
final int userId = createManagedProfile();
@@ -486,6 +519,7 @@ public class UserLifecycleTests {
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -500,11 +534,13 @@ public class UserLifecycleTests {
mRunner.pauseTiming();
final int userId = createManagedProfile();
startUserInBackgroundAndWaitForUnlock(userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
stopUser(userId, true);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -523,11 +559,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -546,11 +584,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
diff --git a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
index 14a3f8f9245b..93b06e84e130 100644
--- a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
+++ b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
@@ -80,21 +80,21 @@ data_sources {
atrace_apps: "*"
atrace_categories: "am"
+ atrace_categories: "binder_driver"
atrace_categories: "bionic"
- atrace_categories: "camera"
- atrace_categories: "wm"
atrace_categories: "dalvik"
- atrace_categories: "sched"
- atrace_categories: "freq"
- atrace_categories: "gfx"
- atrace_categories: "view"
- atrace_categories: "webview"
atrace_categories: "input"
- atrace_categories: "hal"
- atrace_categories: "binder_driver"
+ atrace_categories: "pm"
+ atrace_categories: "res"
+ atrace_categories: "rro"
+ atrace_categories: "ss"
+ atrace_categories: "view"
+ atrace_categories: "wm"
+
+ atrace_categories: "freq"
+ atrace_categories: "sched"
atrace_categories: "sync"
atrace_categories: "workq"
- atrace_categories: "res"
}
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index e1928615211a..73bff08c626d 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -266,9 +266,9 @@ public final class BenchmarkState {
public void sendFullStatusReport(Instrumentation instrumentation, String key) {
Log.i(TAG, key + summaryLine());
Bundle status = new Bundle();
- status.putLong(key + "_median", median());
- status.putLong(key + "_mean", mean());
- status.putLong(key + "_min", min());
+ status.putLong(key + "_median (ns)", median());
+ status.putLong(key + "_mean (ns)", mean());
+ status.putLong(key + "_min (ns)", min());
status.putLong(key + "_standardDeviation", standardDeviation());
instrumentation.sendStatus(Activity.RESULT_OK, status);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 144536ec2644..646a0279c722 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -324,6 +324,7 @@ public class JobInfo implements Parcelable {
private final NetworkRequest networkRequest;
private final long networkDownloadBytes;
private final long networkUploadBytes;
+ private final long minimumNetworkChunkBytes;
private final long minLatencyMillis;
private final long maxExecutionDelayMillis;
private final boolean isPeriodic;
@@ -515,6 +516,17 @@ public class JobInfo implements Parcelable {
}
/**
+ * Return the smallest piece of data that cannot be easily paused and resumed, in bytes.
+ *
+ * @return Smallest piece of data that cannot be easily paused and resumed, or
+ * {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+ * @see Builder#setMinimumNetworkChunkBytes(long)
+ */
+ public @BytesLong long getMinimumNetworkChunkBytes() {
+ return minimumNetworkChunkBytes;
+ }
+
+ /**
* Set for a job that does not recur periodically, to specify a delay after which the job
* will be eligible for execution. This value is not set if the job recurs periodically.
* @see JobInfo.Builder#setMinimumLatency(long)
@@ -679,6 +691,9 @@ public class JobInfo implements Parcelable {
if (networkUploadBytes != j.networkUploadBytes) {
return false;
}
+ if (minimumNetworkChunkBytes != j.minimumNetworkChunkBytes) {
+ return false;
+ }
if (minLatencyMillis != j.minLatencyMillis) {
return false;
}
@@ -741,6 +756,7 @@ public class JobInfo implements Parcelable {
}
hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
+ hashCode = 31 * hashCode + Long.hashCode(minimumNetworkChunkBytes);
hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -777,6 +793,7 @@ public class JobInfo implements Parcelable {
}
networkDownloadBytes = in.readLong();
networkUploadBytes = in.readLong();
+ minimumNetworkChunkBytes = in.readLong();
minLatencyMillis = in.readLong();
maxExecutionDelayMillis = in.readLong();
isPeriodic = in.readInt() == 1;
@@ -807,6 +824,7 @@ public class JobInfo implements Parcelable {
networkRequest = b.mNetworkRequest;
networkDownloadBytes = b.mNetworkDownloadBytes;
networkUploadBytes = b.mNetworkUploadBytes;
+ minimumNetworkChunkBytes = b.mMinimumNetworkChunkBytes;
minLatencyMillis = b.mMinLatencyMillis;
maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
isPeriodic = b.mIsPeriodic;
@@ -851,6 +869,7 @@ public class JobInfo implements Parcelable {
}
out.writeLong(networkDownloadBytes);
out.writeLong(networkUploadBytes);
+ out.writeLong(minimumNetworkChunkBytes);
out.writeLong(minLatencyMillis);
out.writeLong(maxExecutionDelayMillis);
out.writeInt(isPeriodic ? 1 : 0);
@@ -986,6 +1005,7 @@ public class JobInfo implements Parcelable {
private NetworkRequest mNetworkRequest;
private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+ private long mMinimumNetworkChunkBytes = NETWORK_BYTES_UNKNOWN;
private ArrayList<TriggerContentUri> mTriggerContentUris;
private long mTriggerContentUpdateDelay = -1;
private long mTriggerContentMaxDelay = -1;
@@ -1038,6 +1058,7 @@ public class JobInfo implements Parcelable {
mNetworkRequest = job.getRequiredNetwork();
mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+ mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
mTriggerContentUris = job.getTriggerContentUris() != null
? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null;
mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay();
@@ -1256,6 +1277,39 @@ public class JobInfo implements Parcelable {
}
/**
+ * Set the minimum size of non-resumable network traffic this job requires, in bytes. When
+ * the upload or download can be easily paused and resumed, use this to set the smallest
+ * size that must be transmitted between start and stop events to be considered successful.
+ * If the transfer cannot be paused and resumed, then this should be the sum of the values
+ * provided to {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)}.
+ *
+ * <p>
+ * Apps are encouraged to provide values that are as accurate as possible since JobScheduler
+ * will try to run the job at a time when at least the minimum chunk can be transmitted to
+ * reduce the amount of repetitive data that's transferred. Jobs that cannot provide
+ * reasonable estimates should use the sentinel value {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
+ *
+ * <p>
+ * The values provided here only reflect the minimum non-resumable traffic that will be
+ * performed by the base job; if you're using {@link JobWorkItem} then
+ * you also need to define the network traffic used by each work item
+ * when constructing them.
+ *
+ * @param chunkSizeBytes The smallest piece of data that cannot be easily paused and
+ * resumed, in bytes.
+ * @see JobInfo#getMinimumNetworkChunkBytes()
+ * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long, long)
+ */
+ @NonNull
+ public Builder setMinimumNetworkChunkBytes(@BytesLong long chunkSizeBytes) {
+ if (chunkSizeBytes != NETWORK_BYTES_UNKNOWN && chunkSizeBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
+ mMinimumNetworkChunkBytes = chunkSizeBytes;
+ return this;
+ }
+
+ /**
* Specify that to run this job, the device must be charging (or be a
* non-battery-powered device connected to permanent power, such as Android TV
* devices). This defaults to {@code false}.
@@ -1647,12 +1701,29 @@ public class JobInfo implements Parcelable {
/**
* @hide
*/
- public void enforceValidity() {
- // Check that network estimates require network type
- if ((networkDownloadBytes > 0 || networkUploadBytes > 0) && networkRequest == null) {
+ public final void enforceValidity() {
+ // Check that network estimates require network type and are reasonable values.
+ if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0)
+ && networkRequest == null) {
throw new IllegalArgumentException(
"Can't provide estimated network usage without requiring a network");
}
+ final long estimatedTransfer;
+ if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ estimatedTransfer = networkDownloadBytes;
+ } else {
+ estimatedTransfer = networkUploadBytes
+ + (networkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : networkDownloadBytes);
+ }
+ if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN
+ && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+ && minimumNetworkChunkBytes > estimatedTransfer) {
+ throw new IllegalArgumentException(
+ "Minimum chunk size can't be greater than estimated network usage");
+ }
+ if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN && minimumNetworkChunkBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
// Check that a deadline was not set on a periodic job.
if (isPeriodic) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
index 0c45cbf6dc11..372f9faacd5a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
@@ -19,6 +19,7 @@ package android.app.job;
import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
import android.annotation.BytesLong;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Build;
@@ -33,8 +34,9 @@ import android.os.Parcelable;
final public class JobWorkItem implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final Intent mIntent;
- final long mNetworkDownloadBytes;
- final long mNetworkUploadBytes;
+ private final long mNetworkDownloadBytes;
+ private final long mNetworkUploadBytes;
+ private final long mMinimumChunkBytes;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mDeliveryCount;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -49,9 +51,7 @@ final public class JobWorkItem implements Parcelable {
* @param intent The general Intent describing this work.
*/
public JobWorkItem(Intent intent) {
- mIntent = intent;
- mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
- mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+ this(intent, NETWORK_BYTES_UNKNOWN, NETWORK_BYTES_UNKNOWN);
}
/**
@@ -68,9 +68,45 @@ final public class JobWorkItem implements Parcelable {
* uploaded by this job work item, in bytes.
*/
public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
+ this(intent, downloadBytes, uploadBytes, NETWORK_BYTES_UNKNOWN);
+ }
+
+ /**
+ * Create a new piece of work, which can be submitted to
+ * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+ * <p>
+ * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+ * details about how to estimate network traffic.
+ *
+ * @param intent The general Intent describing this work.
+ * @param downloadBytes The estimated size of network traffic that will be
+ * downloaded by this job work item, in bytes.
+ * @param uploadBytes The estimated size of network traffic that will be
+ * uploaded by this job work item, in bytes.
+ * @param minimumChunkBytes The smallest piece of data that cannot be easily paused and
+ * resumed, in bytes.
+ */
+ public JobWorkItem(@Nullable Intent intent, @BytesLong long downloadBytes,
+ @BytesLong long uploadBytes, @BytesLong long minimumChunkBytes) {
+ if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && minimumChunkBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
+ final long estimatedTransfer;
+ if (uploadBytes == NETWORK_BYTES_UNKNOWN) {
+ estimatedTransfer = downloadBytes;
+ } else {
+ estimatedTransfer = uploadBytes
+ + (downloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : downloadBytes);
+ }
+ if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+ && minimumChunkBytes > estimatedTransfer) {
+ throw new IllegalArgumentException(
+ "Minimum chunk size can't be greater than estimated network usage");
+ }
mIntent = intent;
mNetworkDownloadBytes = downloadBytes;
mNetworkUploadBytes = uploadBytes;
+ mMinimumChunkBytes = minimumChunkBytes;
}
/**
@@ -103,6 +139,16 @@ final public class JobWorkItem implements Parcelable {
}
/**
+ * Return the smallest piece of data that cannot be easily paused and resumed, in bytes.
+ *
+ * @return Smallest piece of data that cannot be easily paused and resumed, or
+ * {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+ */
+ public @BytesLong long getMinimumNetworkChunkBytes() {
+ return mMinimumChunkBytes;
+ }
+
+ /**
* Return the count of the number of times this work item has been delivered
* to the job. The value will be > 1 if it has been redelivered because the job
* was stopped or crashed while it had previously been delivered but before the
@@ -161,6 +207,10 @@ final public class JobWorkItem implements Parcelable {
sb.append(" uploadBytes=");
sb.append(mNetworkUploadBytes);
}
+ if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN) {
+ sb.append(" minimumChunkBytes=");
+ sb.append(mMinimumChunkBytes);
+ }
if (mDeliveryCount != 0) {
sb.append(" dcount=");
sb.append(mDeliveryCount);
@@ -169,6 +219,28 @@ final public class JobWorkItem implements Parcelable {
return sb.toString();
}
+ /**
+ * @hide
+ */
+ public void enforceValidity() {
+ final long estimatedTransfer;
+ if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ estimatedTransfer = mNetworkDownloadBytes;
+ } else {
+ estimatedTransfer = mNetworkUploadBytes
+ + (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : mNetworkDownloadBytes);
+ }
+ if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN
+ && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+ && mMinimumChunkBytes > estimatedTransfer) {
+ throw new IllegalArgumentException(
+ "Minimum chunk size can't be greater than estimated network usage");
+ }
+ if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN && mMinimumChunkBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
+ }
+
public int describeContents() {
return 0;
}
@@ -182,6 +254,7 @@ final public class JobWorkItem implements Parcelable {
}
out.writeLong(mNetworkDownloadBytes);
out.writeLong(mNetworkUploadBytes);
+ out.writeLong(mMinimumChunkBytes);
out.writeInt(mDeliveryCount);
out.writeInt(mWorkId);
}
@@ -206,6 +279,7 @@ final public class JobWorkItem implements Parcelable {
}
mNetworkDownloadBytes = in.readLong();
mNetworkUploadBytes = in.readLong();
+ mMinimumChunkBytes = in.readLong();
mDeliveryCount = in.readInt();
mWorkId = in.readInt();
}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/OWNERS b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
new file mode 100644
index 000000000000..217a5edff08b
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS \ No newline at end of file
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 8c06338560bf..968c6e5d9cf6 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -71,6 +71,13 @@ public interface AppStandbyInternal {
long getTimeSinceLastJobRun(String packageName, int userId);
+ /**
+ * Returns the time (in milliseconds) since the app was last interacted with by the user.
+ * This can be larger than the current elapsedRealtime, in case it happened before boot or
+ * a really large value if the app was never interacted with.
+ */
+ long getTimeSinceLastUsedByUser(String packageName, int userId);
+
void onUserRemoved(int userId);
void addListener(AppIdleStateChangeListener listener);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 5a13a849a352..143c0f1ffdf5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -804,28 +804,42 @@ class JobConcurrencyManager {
@WorkType final int workType) {
final List<StateController> controllers = mService.mControllers;
final int numControllers = controllers.size();
- for (int ic = 0; ic < numControllers; ic++) {
- controllers.get(ic).prepareForExecutionLocked(jobStatus);
- }
- final PackageStats packageStats =
- getPkgStatsLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
- packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
- if (!worker.executeRunnableJob(jobStatus, workType)) {
- Slog.e(TAG, "Error executing " + jobStatus);
- mWorkCountTracker.onStagedJobFailed(workType);
+ final PowerManager.WakeLock wl =
+ mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, jobStatus.getTag());
+ wl.setWorkSource(mService.deriveWorkSource(
+ jobStatus.getSourceUid(), jobStatus.getSourcePackageName()));
+ wl.setReferenceCounted(false);
+ // Since the quota controller will start counting from the time prepareForExecutionLocked()
+ // is called, hold a wakelock to make sure the CPU doesn't suspend between that call and
+ // when the service actually starts.
+ wl.acquire();
+ try {
for (int ic = 0; ic < numControllers; ic++) {
- controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ controllers.get(ic).prepareForExecutionLocked(jobStatus);
}
- } else {
- mRunningJobs.add(jobStatus);
- mWorkCountTracker.onJobStarted(workType);
- packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
- mActivePkgStats.add(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats);
- }
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
- if (pendingJobs.remove(jobStatus)) {
- mService.mJobPackageTracker.noteNonpending(jobStatus);
+ final PackageStats packageStats = getPkgStatsLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
+ if (!worker.executeRunnableJob(jobStatus, workType)) {
+ Slog.e(TAG, "Error executing " + jobStatus);
+ mWorkCountTracker.onStagedJobFailed(workType);
+ for (int ic = 0; ic < numControllers; ic++) {
+ controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ }
+ } else {
+ mRunningJobs.add(jobStatus);
+ mWorkCountTracker.onJobStarted(workType);
+ packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
+ mActivePkgStats.add(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+ packageStats);
+ }
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ if (pendingJobs.remove(jobStatus)) {
+ mService.mJobPackageTracker.noteNonpending(jobStatus);
+ }
+ } finally {
+ wl.release();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 78670c7c73b1..f3b2a962bbe8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -143,6 +143,7 @@ import java.util.function.Predicate;
*
* Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
* Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
+ *
* @hide
*/
public class JobSchedulerService extends com.android.server.SystemService
@@ -201,7 +202,7 @@ public class JobSchedulerService extends com.android.server.SystemService
};
@VisibleForTesting
- public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
+ public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
@Override
public long millis() {
return SystemClock.elapsedRealtime();
@@ -315,7 +316,7 @@ public class JobSchedulerService extends com.android.server.SystemService
/**
* Which uids are currently performing backups, so we shouldn't allow their jobs to run.
*/
- final SparseIntArray mBackingUpUids = new SparseIntArray();
+ private final SparseBooleanArray mBackingUpUids = new SparseBooleanArray();
/**
* Cache of debuggable app status.
@@ -601,7 +602,7 @@ public class JobSchedulerService extends com.android.server.SystemService
KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
+ KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
@@ -812,9 +813,10 @@ public class JobSchedulerService extends com.android.server.SystemService
try {
final int userId = UserHandle.getUserId(pkgUid);
IPackageManager pm = AppGlobals.getPackageManager();
- final int state = pm.getApplicationEnabledSetting(pkgName, userId);
+ final int state =
+ pm.getApplicationEnabledSetting(pkgName, userId);
if (state == COMPONENT_ENABLED_STATE_DISABLED
- || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
@@ -830,7 +832,7 @@ public class JobSchedulerService extends com.android.server.SystemService
"app disabled");
}
}
- } catch (RemoteException|IllegalArgumentException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
/*
* IllegalArgumentException means that the package doesn't exist.
* This arises when PACKAGE_CHANGED broadcast delivery has lagged
@@ -866,16 +868,15 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
} else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
- int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (DEBUG) {
- Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+ Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")");
}
synchronized (mLock) {
- mUidToPackageCache.remove(uidRemoved);
+ mUidToPackageCache.remove(pkgUid);
// There's no guarantee that the process has been stopped by the time we
// get here, but since this is generally a user-initiated action, it should
// be fine to just put USER instead of UNINSTALL or DISABLED.
- cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
+ cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
for (int c = 0; c < mControllers.size(); ++c) {
@@ -984,8 +985,18 @@ public class JobSchedulerService extends com.android.server.SystemService
return mConstants;
}
- public boolean isChainedAttributionEnabled() {
- return WorkSource.isChainedBatteryAttributionEnabled(getContext());
+ @NonNull
+ public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
+ if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
+ WorkSource ws = new WorkSource();
+ ws.createWorkChain()
+ .addNode(sourceUid, sourcePackageName)
+ .addNode(Process.SYSTEM_UID, "JobScheduler");
+ return ws;
+ } else {
+ return sourcePackageName == null
+ ? new WorkSource(sourceUid) : new WorkSource(sourceUid, sourcePackageName);
+ }
}
@Nullable
@@ -1133,7 +1144,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
Slog.w(TAG, "Too many jobs for uid " + uId);
throw new IllegalStateException("Apps may not schedule more than "
- + MAX_JOBS_PER_APP + " distinct jobs");
+ + MAX_JOBS_PER_APP + " distinct jobs");
}
}
@@ -1669,6 +1680,7 @@ public class JobSchedulerService extends com.android.server.SystemService
/**
* Called when we want to remove a JobStatus object that we've finished executing.
+ *
* @return true if the job was removed.
*/
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
@@ -1679,7 +1691,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
if (removed && mReadyToRock) {
- for (int i=0; i<mControllers.size(); i++) {
+ for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
}
@@ -1723,7 +1735,6 @@ public class JobSchedulerService extends com.android.server.SystemService
* @param failureToReschedule Provided job status that we will reschedule.
* @return A newly instantiated JobStatus with the same constraints as the last job except
* with adjusted timing constraints.
- *
* @see #maybeQueueReadyJobsForExecutionLocked
*/
@VisibleForTesting
@@ -1765,7 +1776,7 @@ public class JobSchedulerService extends com.android.server.SystemService
newJob.setOriginalLatestRunTimeElapsed(
failureToReschedule.getOriginalLatestRunTimeElapsed());
}
- for (int ic=0; ic<mControllers.size(); ic++) {
+ for (int ic = 0; ic < mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailureLocked(newJob, failureToReschedule);
}
@@ -2283,6 +2294,7 @@ public class JobSchedulerService extends com.android.server.SystemService
runnableJobs.clear();
}
}
+
private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
@GuardedBy("mLock")
@@ -2342,7 +2354,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobExists = mJobs.containsJob(job);
final boolean userStarted = areUsersStartedLocked(job);
- final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
+ final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
@@ -2417,7 +2429,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobExists = mJobs.containsJob(job);
final boolean userStarted = areUsersStartedLocked(job);
- final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
+ final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
@@ -2537,7 +2549,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// No need to actually do anything here, since for a full backup the
// activity manager will kill the process which will kill the job (and
// cause it to restart, but now it can't run).
- mBackingUpUids.put(uid, uid);
+ mBackingUpUids.put(uid, true);
}
}
@@ -2675,7 +2687,8 @@ public class JobSchedulerService extends com.android.server.SystemService
* Binder stub trampoline implementation
*/
final class JobSchedulerStub extends IJobScheduler.Stub {
- /** Cache determination of whether a given app can persist jobs
+ /**
+ * Cache determination of whether a given app can persist jobs
* key is uid of the calling app; value is undetermined/true/false
*/
private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
@@ -2793,6 +2806,7 @@ public class JobSchedulerService extends com.android.server.SystemService
throw new NullPointerException("work is null");
}
+ work.enforceValidity();
validateJobFlags(job, uid);
final long ident = Binder.clearCallingIdentity();
@@ -2956,8 +2970,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public List<JobInfo> getStartedJobs() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "getStartedJobs() is system internal use only.");
+ throw new SecurityException("getStartedJobs() is system internal use only.");
}
final ArrayList<JobInfo> runningJobs;
@@ -2985,8 +2998,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "getAllJobSnapshots() is system internal use only.");
+ throw new SecurityException("getAllJobSnapshots() is system internal use only.");
}
synchronized (mLock) {
final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
@@ -3048,7 +3060,7 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
boolean foundSome = false;
- for (int i=0; i<mActiveServices.size(); i++) {
+ for (int i = 0; i < mActiveServices.size(); i++) {
final JobServiceContext jc = mActiveServices.get(i);
final JobStatus js = jc.getRunningJobLocked();
if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
@@ -3187,7 +3199,7 @@ public class JobSchedulerService extends com.android.server.SystemService
printed = true;
pw.println("source-user-stopped");
}
- if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
+ if (mBackingUpUids.get(js.getSourceUid())) {
if (printed) {
pw.print(" ");
}
@@ -3351,7 +3363,7 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(" !active=");
pw.print(!mConcurrencyManager.isJobRunningLocked(job));
pw.print(" !backingup=");
- pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
+ pw.print(!(mBackingUpUids.get(job.getSourceUid())));
pw.print(" comp=");
pw.print(isComponentUsable(job));
pw.println(")");
@@ -3364,7 +3376,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
pw.decreaseIndent();
- for (int i=0; i<mControllers.size(); i++) {
+ for (int i = 0; i < mControllers.size(); i++) {
pw.println();
pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
pw.increaseIndent();
@@ -3373,7 +3385,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
boolean overridePrinted = false;
- for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ for (int i = 0; i < mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
if (!overridePrinted) {
@@ -3440,7 +3452,7 @@ public class JobSchedulerService extends com.android.server.SystemService
boolean pendingPrinted = false;
pw.println("Pending queue:");
pw.increaseIndent();
- for (int i=0; i<mPendingJobs.size(); i++) {
+ for (int i = 0; i < mPendingJobs.size(); i++) {
JobStatus job = mPendingJobs.get(i);
if (!predicate.test(job)) {
continue;
@@ -3590,7 +3602,8 @@ public class JobSchedulerService extends com.android.server.SystemService
continue;
}
- job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
+ job.dump(proto,
+ JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
proto.write(
JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
@@ -3607,7 +3620,7 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
mConcurrencyManager.isJobRunningLocked(job));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
- mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
+ mBackingUpUids.get(job.getSourceUid()));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
isComponentUsable(job));
@@ -3628,7 +3641,7 @@ public class JobSchedulerService extends com.android.server.SystemService
controller.dumpControllerStateLocked(
proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
}
- for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ for (int i = 0; i < mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
@@ -3667,8 +3680,8 @@ public class JobSchedulerService extends com.android.server.SystemService
if (job == null) {
final long ijToken = proto.start(ActiveJob.INACTIVE);
- proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
- nowElapsed - jsc.mStoppedTime);
+ proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
+ nowElapsed - jsc.mStoppedTime);
if (jsc.mStoppedReason != null) {
proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
jsc.mStoppedReason);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 3fa1c12ce762..2ebd1ade1312 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -42,7 +42,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.WorkSource;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -109,6 +108,7 @@ public final class JobServiceContext implements ServiceConnection {
private final Object mLock;
private final IBatteryStats mBatteryStats;
private final JobPackageTracker mJobPackageTracker;
+ private final PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
// Execution state.
@@ -205,6 +205,7 @@ public final class JobServiceContext implements ServiceConnection {
mCallbackHandler = new JobServiceHandler(looper);
mJobConcurrencyManager = concurrencyManager;
mCompletedListener = service;
+ mPowerManager = mContext.getSystemService(PowerManager.class);
mAvailable = true;
mVerb = VERB_FINISHED;
mPreferredUid = NO_PREFERRED_UID;
@@ -271,6 +272,12 @@ public final class JobServiceContext implements ServiceConnection {
// it was inflated from disk with not-yet-coherent delay/deadline bounds.
job.clearPersistedUtcTimes();
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, job.getTag());
+ mWakeLock.setWorkSource(
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mWakeLock.setReferenceCounted(false);
+ mWakeLock.acquire();
+
mVerb = VERB_BINDING;
scheduleOpTimeOutLocked();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
@@ -306,6 +313,7 @@ public final class JobServiceContext implements ServiceConnection {
mRunningCallback = null;
mParams = null;
mExecutionStartTimeElapsed = 0L;
+ mWakeLock.release();
mVerb = VERB_FINISHED;
removeOpTimeOutLocked();
return false;
@@ -495,42 +503,10 @@ public final class JobServiceContext implements ServiceConnection {
return;
}
this.service = IJobService.Stub.asInterface(service);
- final PowerManager pm =
- (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- runningJob.getTag());
- wl.setWorkSource(deriveWorkSource(runningJob));
- wl.setReferenceCounted(false);
- wl.acquire();
-
- // We use a new wakelock instance per job. In rare cases there is a race between
- // teardown following job completion/cancellation and new job service spin-up
- // such that if we simply assign mWakeLock to be the new instance, we orphan
- // the currently-live lock instead of cleanly replacing it. Watch for this and
- // explicitly fast-forward the release if we're in that situation.
- if (mWakeLock != null) {
- Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
- + " tag=" + mWakeLock.getTag());
- mWakeLock.release();
- }
- mWakeLock = wl;
doServiceBoundLocked();
}
}
- private WorkSource deriveWorkSource(JobStatus runningJob) {
- final int jobUid = runningJob.getSourceUid();
- if (WorkSource.isChainedBatteryAttributionEnabled(mContext)) {
- WorkSource workSource = new WorkSource();
- workSource.createWorkChain()
- .addNode(jobUid, null)
- .addNode(android.os.Process.SYSTEM_UID, "JobScheduler");
- return workSource;
- } else {
- return new WorkSource(jobUid);
- }
- }
-
/** If the client service crashes we reschedule this job and clean up. */
@Override
public void onServiceDisconnected(ComponentName name) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8065aa535f3..9a257725b02b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -549,6 +549,47 @@ public final class ConnectivityController extends RestrictingController implemen
*/
private boolean isInsane(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
+ // Use the maximum possible time since it gives us an upper bound, even though the job
+ // could end up stopping earlier.
+ final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
+
+ final long minimumChunkBytes = jobStatus.getMinimumNetworkChunkBytes();
+ if (minimumChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ final long bandwidthDown = capabilities.getLinkDownstreamBandwidthKbps();
+ // If we don't know the bandwidth, all we can do is hope the job finishes the minimum
+ // chunk in time.
+ if (bandwidthDown > 0) {
+ // Divide by 8 to convert bits to bytes.
+ final long estimatedMillis = ((minimumChunkBytes * DateUtils.SECOND_IN_MILLIS)
+ / (DataUnit.KIBIBYTES.toBytes(bandwidthDown) / 8));
+ if (estimatedMillis > maxJobExecutionTimeMs) {
+ // If we'd never finish the minimum chunk before the timeout, we'd be insane!
+ Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over "
+ + bandwidthDown + " kbps network would take "
+ + estimatedMillis + "ms and job has "
+ + maxJobExecutionTimeMs + "ms to run; that's insane!");
+ return true;
+ }
+ }
+ final long bandwidthUp = capabilities.getLinkUpstreamBandwidthKbps();
+ // If we don't know the bandwidth, all we can do is hope the job finishes in time.
+ if (bandwidthUp > 0) {
+ // Divide by 8 to convert bits to bytes.
+ final long estimatedMillis = ((minimumChunkBytes * DateUtils.SECOND_IN_MILLIS)
+ / (DataUnit.KIBIBYTES.toBytes(bandwidthUp) / 8));
+ if (estimatedMillis > maxJobExecutionTimeMs) {
+ // If we'd never finish the minimum chunk before the timeout, we'd be insane!
+ Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over " + bandwidthUp
+ + " kbps network would take " + estimatedMillis + "ms and job has "
+ + maxJobExecutionTimeMs + "ms to run; that's insane!");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Minimum chunk size isn't defined. Check using the estimated upload/download sizes.
+
if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
&& mChargingTracker.isCharging()) {
// We're charging and on an unmetered network. We don't have to be as conservative about
@@ -557,9 +598,6 @@ public final class ConnectivityController extends RestrictingController implemen
return false;
}
- // Use the maximum possible time since it gives us an upper bound, even though the job
- // could end up stopping earlier.
- final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
@@ -633,23 +671,34 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
- private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
+ private boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
// Only consider doing this for unrestricted prefetching jobs
if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
return false;
}
+ final long estDownloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
+ if (estDownloadBytes <= 0) {
+ // Need to at least know the estimated download bytes for a prefetch job.
+ return false;
+ }
// See if we match after relaxing any unmetered request
final NetworkCapabilities.Builder builder =
copyCapabilities(jobStatus.getJob().getRequiredNetwork());
builder.removeCapability(NET_CAPABILITY_NOT_METERED);
- if (builder.build().satisfiedByNetworkCapabilities(capabilities)) {
- // TODO: treat this as "maybe" response; need to check quotas
- return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
- } else {
- return false;
+ if (builder.build().satisfiedByNetworkCapabilities(capabilities)
+ && jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC) {
+ final long opportunisticQuotaBytes =
+ mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ network, NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS);
+ final long estUploadBytes = jobStatus.getEstimatedNetworkUploadBytes();
+ final long estimatedBytes = estDownloadBytes
+ + (estUploadBytes == JobInfo.NETWORK_BYTES_UNKNOWN ? 0 : estUploadBytes);
+ return opportunisticQuotaBytes >= estimatedBytes;
}
+
+ return false;
}
@VisibleForTesting
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 95eb220cfeb4..fda57f72eb08 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -391,6 +391,7 @@ public final class JobStatus {
private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
/////// Booleans that track if a job is ready to run. They should be updated whenever dependent
/////// states change.
@@ -531,7 +532,7 @@ public final class JobStatus {
mInternalFlags = internalFlags;
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
if (job.getRequiredNetwork() != null) {
// Later, when we check if a given network satisfies the required
@@ -664,7 +665,7 @@ public final class JobStatus {
sourcePackageName, sourceUserId, toShortString()));
}
pendingWork.add(work);
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
}
public JobWorkItem dequeueWorkLocked() {
@@ -677,7 +678,7 @@ public final class JobStatus {
executingWork.add(work);
work.bumpDeliveryCount();
}
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
return work;
}
return null;
@@ -736,7 +737,7 @@ public final class JobStatus {
pendingWork = null;
executingWork = null;
incomingJob.nextPendingWorkId = nextPendingWorkId;
- incomingJob.updateEstimatedNetworkBytesLocked();
+ incomingJob.updateNetworkBytesLocked();
} else {
// We are completely stopping the job... need to clean up work.
ungrantWorkList(pendingWork);
@@ -744,7 +745,7 @@ public final class JobStatus {
ungrantWorkList(executingWork);
executingWork = null;
}
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
}
public void prepareLocked() {
@@ -944,9 +945,10 @@ public final class JobStatus {
}
}
- private void updateEstimatedNetworkBytesLocked() {
+ private void updateNetworkBytesLocked() {
mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+ mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
if (pendingWork != null) {
for (int i = 0; i < pendingWork.size(); i++) {
@@ -968,6 +970,12 @@ public final class JobStatus {
mTotalNetworkUploadBytes += uploadBytes;
}
}
+ final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
+ if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
+ mMinimumNetworkChunkBytes = chunkBytes;
+ } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes);
+ }
}
}
}
@@ -980,6 +988,10 @@ public final class JobStatus {
return mTotalNetworkUploadBytes;
}
+ public long getMinimumNetworkChunkBytes() {
+ return mMinimumNetworkChunkBytes;
+ }
+
/** Does this job have any sort of networking constraint? */
public boolean hasConnectivityConstraint() {
// No need to check mDynamicConstraints since connectivity will only be in that list if
@@ -1942,6 +1954,10 @@ public final class JobStatus {
pw.print("Network upload bytes: ");
pw.println(mTotalNetworkUploadBytes);
}
+ if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ pw.print("Minimum network chunk bytes: ");
+ pw.println(mMinimumNetworkChunkBytes);
+ }
if (job.getMinLatencyMillis() != 0) {
pw.print("Minimum latency: ");
TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index e8ebfb53fde8..98e37a13b029 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,11 +18,9 @@ package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.IndentingPrintWriter;
@@ -62,8 +60,6 @@ public final class TimeController extends StateController {
private long mNextDelayExpiredElapsedMillis;
private volatile long mLastFiredDelayExpiredElapsedMillis;
- private final boolean mChainedAttributionEnabled;
-
private AlarmManager mAlarmService = null;
/** List of tracked jobs, sorted asc. by deadline */
private final List<JobStatus> mTrackedJobs = new LinkedList<>();
@@ -73,7 +69,6 @@ public final class TimeController extends StateController {
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
- mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
}
/**
@@ -117,7 +112,8 @@ public final class TimeController extends StateController {
it.add(job);
job.setTrackingController(JobStatus.TRACKING_TIME);
- WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
+ WorkSource ws =
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
// Only update alarms if the job would be ready with the relevant timing constraint
// satisfied.
@@ -165,7 +161,7 @@ public final class TimeController extends StateController {
} else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
// This job's deadline is earlier than the current set alarm. Update the alarm.
setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(),
- deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
}
}
if (job.hasTimingDelayConstraint()
@@ -177,7 +173,7 @@ public final class TimeController extends StateController {
&& wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
// This job's delay is earlier than the current set alarm. Update the alarm.
setDelayExpiredAlarmLocked(job.getEarliestRunTime(),
- deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
}
}
}
@@ -248,7 +244,7 @@ public final class TimeController extends StateController {
}
}
setDeadlineExpiredAlarmLocked(nextExpiryTime,
- deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
+ mService.deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
}
}
@@ -312,19 +308,7 @@ public final class TimeController extends StateController {
mStateChangedListener.onControllerStateChanged();
}
setDelayExpiredAlarmLocked(nextDelayTime,
- deriveWorkSource(nextDelayUid, nextDelayPackageName));
- }
- }
-
- private WorkSource deriveWorkSource(int uid, @Nullable String packageName) {
- if (mChainedAttributionEnabled) {
- WorkSource ws = new WorkSource();
- ws.createWorkChain()
- .addNode(uid, packageName)
- .addNode(Process.SYSTEM_UID, "JobScheduler");
- return ws;
- } else {
- return packageName == null ? new WorkSource(uid) : new WorkSource(uid, packageName);
+ mService.deriveWorkSource(nextDelayUid, nextDelayPackageName));
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
index 20df3ea2196b..51931792390c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -66,7 +66,8 @@ public abstract class JobRestriction {
public abstract void dumpConstants(IndentingPrintWriter pw);
/** Dump any internal constants the Restriction may have. */
- public abstract void dumpConstants(ProtoOutputStream proto);
+ public void dumpConstants(ProtoOutputStream proto) {
+ }
/** @return reason code for the Restriction. */
@JobParameters.StopReason
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index 6d67ee37d8e6..5695a94f5c25 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -20,16 +20,21 @@ import android.app.job.JobParameters;
import android.os.PowerManager;
import android.os.PowerManager.OnThermalStatusChangedListener;
import android.util.IndentingPrintWriter;
-import android.util.proto.ProtoOutputStream;
import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobSchedulerServiceDumpProto;
import com.android.server.job.controllers.JobStatus;
public class ThermalStatusRestriction extends JobRestriction {
private static final String TAG = "ThermalStatusRestriction";
- private volatile boolean mIsThermalRestricted = false;
+ /** The threshold at which we start restricting non-EJ jobs. */
+ private static final int REGULAR_JOB_THRESHOLD = PowerManager.THERMAL_STATUS_SEVERE;
+ /** The lowest threshold at which we start restricting jobs. */
+ private static final int LOWER_THRESHOLD = REGULAR_JOB_THRESHOLD;
+ /** The threshold at which we start restricting ALL jobs. */
+ private static final int UPPER_THRESHOLD = PowerManager.THERMAL_STATUS_CRITICAL;
+
+ private volatile int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
private PowerManager mPowerManager;
@@ -47,29 +52,46 @@ public class ThermalStatusRestriction extends JobRestriction {
public void onThermalStatusChanged(int status) {
// This is called on the main thread. Do not do any slow operations in it.
// mService.onControllerStateChanged() will just post a message, which is okay.
- final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
- if (mIsThermalRestricted == shouldBeActive) {
- return;
+
+ // There are three buckets:
+ // 1. Below the lower threshold (we don't care about changes within this bucket)
+ // 2. Between the lower and upper thresholds.
+ // -> We care about transitions across buckets
+ // -> We care about transitions within the middle bucket
+ // 3. Upper the upper threshold (we don't care about changes within this bucket)
+ final boolean significantChange =
+ // Handle transitions within and into the bucket we care about (thus
+ // causing us to change our restrictions).
+ (status >= LOWER_THRESHOLD && status <= UPPER_THRESHOLD)
+ // Take care of transitions from the 2nd or 3rd bucket to the 1st
+ // bucket (thus exiting any restrictions we started enforcing).
+ || (mThermalStatus >= LOWER_THRESHOLD && status < LOWER_THRESHOLD)
+ // Take care of transitions from the 1st or 2nd bucket to the 3rd
+ // bucket (thus resulting in us beginning to enforce the tightest
+ // restrictions).
+ || (mThermalStatus < UPPER_THRESHOLD && status > UPPER_THRESHOLD);
+ mThermalStatus = status;
+ if (significantChange) {
+ mService.onControllerStateChanged();
}
- mIsThermalRestricted = shouldBeActive;
- mService.onControllerStateChanged();
}
});
}
@Override
public boolean isJobRestricted(JobStatus job) {
- return mIsThermalRestricted;
+ if (mThermalStatus >= UPPER_THRESHOLD) {
+ return true;
+ }
+ if (mThermalStatus >= REGULAR_JOB_THRESHOLD) {
+ return !job.shouldTreatAsExpeditedJob();
+ }
+ return false;
}
@Override
public void dumpConstants(IndentingPrintWriter pw) {
- pw.print("In thermal throttling?: ");
- pw.println(mIsThermalRestricted);
- }
-
- @Override
- public void dumpConstants(ProtoOutputStream proto) {
- proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mIsThermalRestricted);
+ pw.print("Thermal status: ");
+ pw.println(mThermalStatus);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
new file mode 100644
index 000000000000..dac03c969dff
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -0,0 +1,957 @@
+/*
+ * 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
+import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT;
+import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION;
+import static com.android.server.tare.EconomicPolicy.TYPE_ACTION;
+import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
+import static com.android.server.tare.EconomicPolicy.eventToString;
+import static com.android.server.tare.EconomicPolicy.getEventType;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.usage.AppStandbyInternal;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.PriorityQueue;
+import java.util.function.Consumer;
+
+/**
+ * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with
+ * ledgers, carrying out specific events such as wealth reclamation, granting initial balances or
+ * replenishing balances, and tracking ongoing events.
+ */
+class Agent {
+ private static final String TAG = "TARE-" + Agent.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * The minimum amount of time an app must not have been used by the user before we start
+ * regularly reclaiming ARCs from it.
+ */
+ private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS;
+ /**
+ * The maximum amount of time we'll keep a transaction around for.
+ * For now, only keep transactions we actually have a use for. We can increase it if we want
+ * to use older transactions or provide older transactions to apps.
+ */
+ private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS;
+
+ private static final String ALARM_TAG_LEDGER_CLEANUP = "*tare.ledger_cleanup*";
+ private static final String ALARM_TAG_SOLVENCY_CHECK = "*tare.solvency_check*";
+
+ private final Object mLock;
+ private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private final Handler mHandler;
+ private final InternalResourceService mIrs;
+
+ private final AppStandbyInternal mAppStandbyInternal;
+
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, SparseArrayMap<String, OngoingEvent>>
+ mCurrentOngoingEvents = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private long mCurrentNarcsInCirculation;
+
+ /**
+ * Listener to track and manage when we remove old transactions from ledgers.
+ */
+ @GuardedBy("mLock")
+ private final LedgerCleanupAlarmListener mLedgerCleanupAlarmListener =
+ new LedgerCleanupAlarmListener();
+
+ /**
+ * Listener to track and manage when apps will cross the solvency threshold (in both
+ * directions).
+ */
+ @GuardedBy("mLock")
+ private final SolvencyAlarmListener mSolvencyAlarmListener = new SolvencyAlarmListener();
+
+ private static final int MSG_CHECK_BALANCE = 0;
+ private static final int MSG_CLEAN_LEDGER = 1;
+ private static final int MSG_SET_ALARMS = 2;
+
+ Agent(@NonNull InternalResourceService irs,
+ @NonNull CompleteEconomicPolicy completeEconomicPolicy) {
+ mLock = irs.getLock();
+ mIrs = irs;
+ mCompleteEconomicPolicy = completeEconomicPolicy;
+ mHandler = new AgentHandler(TareHandlerThread.get().getLooper());
+ mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = mLedgers.get(userId, pkgName);
+ if (ledger == null) {
+ // TODO: load from disk
+ ledger = new Ledger();
+ mLedgers.add(userId, pkgName, ledger);
+ }
+ return ledger;
+ }
+
+ private class TotalDeltaCalculator implements Consumer<OngoingEvent> {
+ private Ledger mLedger;
+ private long mNowElapsed;
+ private long mNow;
+ private long mTotal;
+
+ void reset(@NonNull Ledger ledger, long nowElapsed, long now) {
+ mLedger = ledger;
+ mNowElapsed = nowElapsed;
+ mNow = now;
+ mTotal = 0;
+ }
+
+ @Override
+ public void accept(OngoingEvent ongoingEvent) {
+ mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private final TotalDeltaCalculator mTotalDeltaCalculator = new TotalDeltaCalculator();
+
+ /** Get an app's current balance, factoring in any currently ongoing events. */
+ @GuardedBy("mLock")
+ long getBalanceLocked(final int userId, @NonNull final String pkgName) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ long balance = ledger.getCurrentBalance();
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents != null) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = System.currentTimeMillis();
+ mTotalDeltaCalculator.reset(ledger, nowElapsed, now);
+ ongoingEvents.forEach(mTotalDeltaCalculator);
+ balance += mTotalDeltaCalculator.mTotal;
+ }
+ return balance;
+ }
+
+ @GuardedBy("mLock")
+ void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
+ final int eventId, @Nullable String tag) {
+ final long now = System.currentTimeMillis();
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final boolean wasSolvent = getBalanceLocked(userId, pkgName) > 0;
+
+ final int eventType = getEventType(eventId);
+ switch (eventType) {
+ case TYPE_ACTION:
+ final long actionCost =
+ mCompleteEconomicPolicy.getCostOfAction(eventId, userId, pkgName);
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, eventId, tag, -actionCost));
+ break;
+
+ case TYPE_REWARD:
+ final EconomicPolicy.Reward reward = mCompleteEconomicPolicy.getReward(eventId);
+ if (reward != null) {
+ final long rewardSum = ledger.get24HourSum(eventId, now);
+ final long rewardVal = Math.max(0,
+ Math.min(reward.maxDailyReward - rewardSum, reward.instantReward));
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, eventId, tag, rewardVal));
+ }
+ break;
+
+ default:
+ Slog.w(TAG, "Unsupported event type: " + eventType);
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+
+ final boolean isSolvent = getBalanceLocked(userId, pkgName) > 0;
+ if (wasSolvent && !isSolvent) {
+ mIrs.postSolvencyChanged(userId, pkgName, false);
+ } else if (!wasSolvent && isSolvent) {
+ mIrs.postSolvencyChanged(userId, pkgName, true);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId,
+ @Nullable String tag, final long startElapsed) {
+ noteOngoingEventLocked(userId, pkgName, eventId, tag, startElapsed, true);
+ }
+
+ @GuardedBy("mLock")
+ void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId,
+ @Nullable String tag, final long startElapsed, final boolean updateBalanceCheck) {
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents == null) {
+ ongoingEvents = new SparseArrayMap<>();
+ mCurrentOngoingEvents.add(userId, pkgName, ongoingEvents);
+ }
+ OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
+
+ final int eventType = getEventType(eventId);
+ switch (eventType) {
+ case TYPE_ACTION:
+ final long actionCost =
+ mCompleteEconomicPolicy.getCostOfAction(eventId, userId, pkgName);
+
+ if (ongoingEvent == null) {
+ ongoingEvents.add(eventId, tag,
+ new OngoingEvent(eventId, tag, null, startElapsed, -actionCost));
+ } else {
+ ongoingEvent.refCount++;
+ }
+ break;
+
+ case TYPE_REWARD:
+ final EconomicPolicy.Reward reward = mCompleteEconomicPolicy.getReward(eventId);
+ if (reward != null) {
+ if (ongoingEvent == null) {
+ ongoingEvents.add(eventId, tag, new OngoingEvent(
+ eventId, tag, reward, startElapsed, reward.ongoingRewardPerSecond));
+ } else {
+ ongoingEvent.refCount++;
+ }
+ }
+ break;
+
+ default:
+ Slog.w(TAG, "Unsupported event type: " + eventType);
+ }
+
+ if (updateBalanceCheck) {
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void updateOngoingEventsLocked() {
+ final long now = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
+ ongoingEvents.forEach((ongoingEvent) -> {
+ stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
+ ongoingEvent.tag, nowElapsed, now, false);
+ noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ nowElapsed, false);
+ });
+ scheduleBalanceCheckLocked(userId, pkgName);
+ });
+ }
+
+ @GuardedBy("mLock")
+ void updateOngoingEventsLocked(final int userId, @NonNull ArraySet<String> pkgNames) {
+ final long now = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ for (int i = 0; i < pkgNames.size(); ++i) {
+ final String pkgName = pkgNames.valueAt(i);
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents != null) {
+ ongoingEvents.forEach((ongoingEvent) -> {
+ stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
+ ongoingEvent.tag, nowElapsed, now, false);
+ noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ nowElapsed, false);
+ });
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
+ @Nullable String tag, final long nowElapsed, final long now) {
+ stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now, true);
+ }
+
+ @GuardedBy("mLock")
+ void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
+ @Nullable String tag, final long nowElapsed, final long now,
+ final boolean updateBalanceCheck) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents == null) {
+ Slog.wtf(TAG, "No ongoing transactions :/");
+ return;
+ }
+ final OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
+ if (ongoingEvent == null) {
+ Slog.wtf(TAG, "Nonexistent ongoing transaction "
+ + eventToString(eventId) + (tag == null ? "" : ":" + tag)
+ + " for <" + userId + ">" + pkgName + " ended");
+ return;
+ }
+ ongoingEvent.refCount--;
+ if (ongoingEvent.refCount <= 0) {
+ final long startElapsed = ongoingEvent.startTimeElapsed;
+ final long startTime = now - (nowElapsed - startElapsed);
+ final long actualDelta = getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(startTime, now, eventId, tag, actualDelta));
+ ongoingEvents.delete(eventId, tag);
+ }
+ if (updateBalanceCheck) {
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private long getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, @NonNull Ledger ledger,
+ long nowElapsed, long now) {
+ final long startElapsed = ongoingEvent.startTimeElapsed;
+ final long durationSecs = (nowElapsed - startElapsed) / 1000;
+ final long computedDelta = durationSecs * ongoingEvent.deltaPerSec;
+ if (ongoingEvent.reward == null) {
+ return computedDelta;
+ }
+ final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now);
+ return Math.max(0,
+ Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
+ }
+
+ @GuardedBy("mLock")
+ private void recordTransactionLocked(final int userId, @NonNull final String pkgName,
+ @NonNull Ledger ledger, @NonNull Ledger.Transaction transaction) {
+ final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
+ final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
+ if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
+ final long newDelta = maxCirculationAllowed - mCurrentNarcsInCirculation;
+ Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
+ + eventToString(transaction.eventId)
+ + (transaction.tag == null ? "" : ":" + transaction.tag)
+ + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.eventId, transaction.tag, newDelta);
+ }
+ final long originalBalance = ledger.getCurrentBalance();
+ if (transaction.delta > 0
+ && originalBalance + transaction.delta
+ > mCompleteEconomicPolicy.getMaxSatiatedBalance()) {
+ final long newDelta = mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance;
+ Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
+ + eventToString(transaction.eventId)
+ + (transaction.tag == null ? "" : ":" + transaction.tag)
+ + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.eventId, transaction.tag, newDelta);
+ }
+ ledger.recordTransaction(transaction);
+ mCurrentNarcsInCirculation += transaction.delta;
+ if (!mLedgerCleanupAlarmListener.hasAlarmScheduledLocked(userId, pkgName)) {
+ // The earliest transaction won't change until we clean up the ledger, so no point
+ // continuing to reschedule an existing cleanup.
+ final long cleanupAlarmElapsed = SystemClock.elapsedRealtime() + MAX_TRANSACTION_AGE_MS
+ - (System.currentTimeMillis() - ledger.getEarliestTransaction().endTimeMs);
+ mLedgerCleanupAlarmListener.addAlarmLocked(userId, pkgName, cleanupAlarmElapsed);
+ }
+ // TODO: save changes to disk in a background thread
+ final long newBalance = ledger.getCurrentBalance();
+ if (originalBalance <= 0 && newBalance > 0) {
+ mIrs.postSolvencyChanged(userId, pkgName, true);
+ } else if (originalBalance > 0 && newBalance <= 0) {
+ mIrs.postSolvencyChanged(userId, pkgName, false);
+ }
+ }
+
+ /**
+ * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The
+ * reclamation will not reduce an app's balance below its minimum balance as dictated by the
+ * EconomicPolicy.
+ *
+ * @param percentage A value between 0 and 1 to indicate how much of the unused balance should
+ * be reclaimed.
+ */
+ @GuardedBy("mLock")
+ void reclaimUnusedAssetsLocked(double percentage) {
+ final List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final long now = System.currentTimeMillis();
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
+ final String pkgName = pkgs.get(i).packageName;
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ // AppStandby only counts elapsed time for things like this
+ // TODO: should we use clock time instead?
+ final long timeSinceLastUsedMs =
+ mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
+ if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) {
+ // Use a constant floor instead of the scaled floor from the IRS.
+ final long minBalance =
+ mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName);
+ final long curBalance = ledger.getCurrentBalance();
+ long toReclaim = (long) (curBalance * percentage);
+ if (curBalance - toReclaim < minBalance) {
+ toReclaim = curBalance - minBalance;
+ }
+ if (toReclaim > 0) {
+ Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
+ + " from <" + userId + ">" + pkgName);
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(
+ now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim));
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void distributeBasicIncomeLocked(int batteryLevel) {
+ List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final long now = System.currentTimeMillis();
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final PackageInfo pkgInfo = pkgs.get(i);
+ final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
+ final String pkgName = pkgInfo.packageName;
+ Ledger ledger = getLedgerLocked(userId, pkgName);
+ final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ final double perc = batteryLevel / 100d;
+ // TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
+ if (ledger.getCurrentBalance() < minBalance) {
+ final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
+ null, (long) (perc * shortfall)));
+ }
+ }
+ }
+
+ /** Give each app an initial balance. */
+ @GuardedBy("mLock")
+ void grantBirthrightsLocked() {
+ UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ final int[] userIds = userManagerInternal.getUserIds();
+ for (int userId : userIds) {
+ grantBirthrightsLocked(userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void grantBirthrightsLocked(final int userId) {
+ PackageManager packageManager = mIrs.getContext().getPackageManager();
+ List<PackageInfo> pkgs = packageManager.getInstalledPackagesAsUser(0, userId);
+ final long maxBirthright =
+ mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
+ final long now = System.currentTimeMillis();
+
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final PackageInfo packageInfo = pkgs.get(i);
+ final String pkgName = packageInfo.packageName;
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() > 0) {
+ // App already got credits somehow. Move along.
+ Slog.wtf(TAG, "App " + pkgName + " had credits before economy was set up");
+ continue;
+ }
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
+ Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))));
+ }
+ }
+
+ @GuardedBy("mLock")
+ void grantBirthrightLocked(final int userId, @NonNull final String pkgName) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() > 0) {
+ Slog.wtf(TAG, "App " + pkgName + " had credits as soon as it was installed");
+ // App already got credits somehow. Move along.
+ return;
+ }
+
+ List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final int numPackages = pkgs.size();
+ final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
+ final long now = System.currentTimeMillis();
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
+ Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))));
+ }
+
+ @GuardedBy("mLock")
+ void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) {
+ reclaimAssetsLocked(userId, pkgName);
+ mLedgerCleanupAlarmListener.removeAlarmLocked(userId, pkgName);
+ mSolvencyAlarmListener.removeAlarmLocked(userId, pkgName);
+ }
+
+ /**
+ * Reclaims any ARCs granted to the app, making them available to other apps. Also deletes the
+ * app's ledger and stops any ongoing event tracking.
+ */
+ @GuardedBy("mLock")
+ private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() != 0) {
+ mCurrentNarcsInCirculation -= ledger.getCurrentBalance();
+ }
+ // TODO: delete ledger entry from disk
+ mLedgers.delete(userId, pkgName);
+ mCurrentOngoingEvents.delete(userId, pkgName);
+ }
+
+ @GuardedBy("mLock")
+ void onUserRemovedLocked(final int userId, @NonNull final List<String> pkgNames) {
+ reclaimAssetsLocked(userId, pkgNames);
+ mLedgerCleanupAlarmListener.removeAlarmsLocked(userId);
+ mSolvencyAlarmListener.removeAlarmsLocked(userId);
+ }
+
+ @GuardedBy("mLock")
+ private void reclaimAssetsLocked(final int userId, @NonNull final List<String> pkgNames) {
+ for (int i = 0; i < pkgNames.size(); ++i) {
+ reclaimAssetsLocked(userId, pkgNames.get(i));
+ }
+ }
+
+ private static class TrendCalculator implements Consumer<OngoingEvent> {
+ private boolean mSolvent;
+ /**
+ * The maximum change in credits per second towards 0 (solvency/insolvency threshold).
+ * A value of 0 means the current ongoing events will never result in the app crossing the
+ * solvency threshold.
+ */
+ private long mMaxDeltaPerSecToThreshold;
+
+ void reset(boolean solvent) {
+ mSolvent = solvent;
+ mMaxDeltaPerSecToThreshold = 0;
+ }
+
+ @Override
+ public void accept(OngoingEvent ongoingEvent) {
+ if ((mSolvent && ongoingEvent.deltaPerSec < 0)
+ || (!mSolvent && ongoingEvent.deltaPerSec > 0)) {
+ mMaxDeltaPerSecToThreshold += ongoingEvent.deltaPerSec;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private final TrendCalculator mTrendCalculator = new TrendCalculator();
+
+ @GuardedBy("mLock")
+ private void scheduleBalanceCheckLocked(final int userId, @NonNull final String pkgName) {
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents == null) {
+ // No ongoing transactions. No reason to schedule
+ mSolvencyAlarmListener.removeAlarmLocked(userId, pkgName);
+ return;
+ }
+ final long balance = getBalanceLocked(userId, pkgName);
+ mTrendCalculator.reset(balance > 0);
+ ongoingEvents.forEach(mTrendCalculator);
+ if (mTrendCalculator.mMaxDeltaPerSecToThreshold == 0) {
+ // Will never cross solvency threshold based on current events.
+ mSolvencyAlarmListener.removeAlarmLocked(userId, pkgName);
+ return;
+ }
+ // The minimum amount of time before this app will cross the solvency threshold.
+ // Including "-" in the calculation ensures that minSeconds is always non-negative:
+ // * If balance is negative (or 0), solvent=false, so the maxDeltaPerSecToThreshold is
+ // positive
+ // * If balance is positive, solvent=true, so the maxDeltaPerSecToThreshold is negative
+ final long minSeconds = -balance / mTrendCalculator.mMaxDeltaPerSecToThreshold;
+ mSolvencyAlarmListener.addAlarmLocked(userId, pkgName,
+ SystemClock.elapsedRealtime() + minSeconds * 1000);
+ }
+
+ private static class OngoingEvent {
+ public final long startTimeElapsed;
+ public final int eventId;
+ @Nullable
+ public final String tag;
+ @Nullable
+ public final EconomicPolicy.Reward reward;
+ public final long deltaPerSec;
+ public int refCount;
+
+ OngoingEvent(int eventId, @Nullable String tag,
+ @Nullable EconomicPolicy.Reward reward, long startTimeElapsed, long deltaPerSec) {
+ this.startTimeElapsed = startTimeElapsed;
+ this.eventId = eventId;
+ this.tag = tag;
+ this.reward = reward;
+ this.deltaPerSec = deltaPerSec;
+ refCount = 1;
+ }
+ }
+
+ /**
+ * An {@link AlarmManager.OnAlarmListener} that will queue up all pending alarms and only
+ * schedule one alarm for the earliest alarm.
+ */
+ private abstract class AlarmQueueListener implements AlarmManager.OnAlarmListener {
+ final class Package {
+ public final String packageName;
+ public final int userId;
+
+ Package(int userId, String packageName) {
+ this.userId = userId;
+ this.packageName = packageName;
+ }
+
+ @Override
+ public String toString() {
+ return "<" + userId + ">" + packageName;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Package) {
+ Package other = (Package) obj;
+ return userId == other.userId && Objects.equals(packageName, other.packageName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return packageName.hashCode() + userId;
+ }
+ }
+
+ class AlarmQueue extends PriorityQueue<Pair<Package, Long>> {
+ AlarmQueue() {
+ super(1, (o1, o2) -> (int) (o1.second - o2.second));
+ }
+
+ boolean contains(@NonNull Package pkg) {
+ Pair[] alarms = toArray(new Pair[size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ if (pkg.equals(alarms[i].first)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove any instances of the Package from the queue.
+ *
+ * @return true if an instance was removed, false otherwise.
+ */
+ boolean remove(@NonNull Package pkg) {
+ boolean removed = false;
+ Pair[] alarms = toArray(new Pair[size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ if (pkg.equals(alarms[i].first)) {
+ remove(alarms[i]);
+ removed = true;
+ }
+ }
+ return removed;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private final AlarmQueue mAlarmQueue = new AlarmQueue();
+ private final String mAlarmTag;
+ /** Whether to use an exact alarm or an inexact alarm. */
+ private final boolean mExactAlarm;
+ /** The minimum amount of time between check alarms. */
+ private final long mMinTimeBetweenAlarmsMs;
+ /** The next time the alarm is set to go off, in the elapsed realtime timebase. */
+ @GuardedBy("mLock")
+ private long mTriggerTimeElapsed = 0;
+
+ protected AlarmQueueListener(@NonNull String alarmTag, boolean exactAlarm,
+ long minTimeBetweenAlarmsMs) {
+ mAlarmTag = alarmTag;
+ mExactAlarm = exactAlarm;
+ mMinTimeBetweenAlarmsMs = minTimeBetweenAlarmsMs;
+ }
+
+ @GuardedBy("mLock")
+ boolean hasAlarmScheduledLocked(int userId, @NonNull String pkgName) {
+ final Package pkg = new Package(userId, pkgName);
+ return mAlarmQueue.contains(pkg);
+ }
+
+ @GuardedBy("mLock")
+ void addAlarmLocked(int userId, @NonNull String pkgName, long alarmTimeElapsed) {
+ final Package pkg = new Package(userId, pkgName);
+ mAlarmQueue.remove(pkg);
+ mAlarmQueue.offer(new Pair<>(pkg, alarmTimeElapsed));
+ setNextAlarmLocked();
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmLocked(@NonNull Package pkg) {
+ if (mAlarmQueue.remove(pkg)) {
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmLocked(int userId, @NonNull String packageName) {
+ removeAlarmLocked(new Package(userId, packageName));
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmsLocked(int userId) {
+ boolean removed = false;
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ final Package pkg = (Package) alarms[i].first;
+ if (userId == pkg.userId) {
+ mAlarmQueue.remove(alarms[i]);
+ removed = true;
+ }
+ }
+ if (removed) {
+ setNextAlarmLocked();
+ }
+ }
+
+ /** Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue. */
+ @GuardedBy("mLock")
+ void setNextAlarmLocked() {
+ setNextAlarmLocked(SystemClock.elapsedRealtime());
+ }
+
+ /**
+ * Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue, using
+ * {@code earliestTriggerElapsed} as a floor.
+ */
+ @GuardedBy("mLock")
+ private void setNextAlarmLocked(long earliestTriggerElapsed) {
+ if (mAlarmQueue.size() > 0) {
+ final Pair<Package, Long> alarm = mAlarmQueue.peek();
+ final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed, alarm.second);
+ // Only schedule the alarm if one of the following is true:
+ // 1. There isn't one currently scheduled
+ // 2. The new alarm is significantly earlier than the previous alarm. If it's
+ // earlier but not significantly so, then we essentially delay the check for some
+ // apps by up to a minute.
+ // 3. The alarm is after the current alarm.
+ if (mTriggerTimeElapsed == 0
+ || nextTriggerTimeElapsed < mTriggerTimeElapsed - MINUTE_IN_MILLIS
+ || mTriggerTimeElapsed < nextTriggerTimeElapsed) {
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed
+ + " for app " + alarm.first);
+ }
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager =
+ mIrs.getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ if (mExactAlarm) {
+ alarmManager.setExact(AlarmManager.ELAPSED_REALTIME,
+ nextTriggerTimeElapsed, mAlarmTag, this, mHandler);
+ } else {
+ alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
+ nextTriggerTimeElapsed, mMinTimeBetweenAlarmsMs / 2,
+ mAlarmTag, this, mHandler);
+ }
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_SET_ALARMS, 30_000);
+ }
+ });
+ mTriggerTimeElapsed = nextTriggerTimeElapsed;
+ }
+ } else {
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager =
+ mIrs.getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ // This should only be null at boot time. No concerns around not
+ // cancelling if we get null here.
+ alarmManager.cancel(this);
+ }
+ });
+ mTriggerTimeElapsed = 0;
+ }
+ }
+
+ @GuardedBy("mLock")
+ protected abstract void processExpiredAlarmLocked(int userId, @NonNull String packageName);
+
+ @Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ while (mAlarmQueue.size() > 0) {
+ final Pair<Package, Long> alarm = mAlarmQueue.peek();
+ if (alarm.second <= nowElapsed) {
+ processExpiredAlarmLocked(alarm.first.userId, alarm.first.packageName);
+ mAlarmQueue.remove(alarm);
+ } else {
+ break;
+ }
+ }
+ setNextAlarmLocked(nowElapsed + mMinTimeBetweenAlarmsMs);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.print(mAlarmTag);
+ pw.println(" alarms:");
+ pw.increaseIndent();
+
+ if (mAlarmQueue.size() == 0) {
+ pw.println("NOT WAITING");
+ } else {
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = 0; i < alarms.length; ++i) {
+ final Package pkg = (Package) alarms[i].first;
+ pw.print(pkg);
+ pw.print(": ");
+ pw.print(alarms[i].second);
+ pw.println();
+ }
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+
+ /** Clean up old transactions from {@link Ledger}s. */
+ private class LedgerCleanupAlarmListener extends AlarmQueueListener {
+ private LedgerCleanupAlarmListener() {
+ // We don't need to run cleanup too frequently.
+ super(ALARM_TAG_LEDGER_CLEANUP, false, HOUR_IN_MILLIS);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
+ mHandler.obtainMessage(MSG_CLEAN_LEDGER, userId, 0, packageName).sendToTarget();
+ }
+ }
+
+ /** Track when apps will cross the solvency threshold (in both directions). */
+ private class SolvencyAlarmListener extends AlarmQueueListener {
+ private SolvencyAlarmListener() {
+ super(ALARM_TAG_SOLVENCY_CHECK, true, 15_000L);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
+ mHandler.obtainMessage(MSG_CHECK_BALANCE, userId, 0, packageName).sendToTarget();
+ }
+ }
+
+ private final class AgentHandler extends Handler {
+ AgentHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CHECK_BALANCE: {
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ synchronized (mLock) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final long loggedBalance = ledger.getCurrentBalance();
+ final long newBalance = getBalanceLocked(userId, pkgName);
+ if (loggedBalance <= 0 && newBalance > 0) {
+ mIrs.postSolvencyChanged(userId, pkgName, true);
+ } else if (loggedBalance > 0 && newBalance <= 0) {
+ mIrs.postSolvencyChanged(userId, pkgName, false);
+ } else {
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+ break;
+
+ case MSG_CLEAN_LEDGER: {
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ synchronized (mLock) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
+ }
+ }
+ break;
+
+ case MSG_SET_ALARMS: {
+ synchronized (mLock) {
+ mLedgerCleanupAlarmListener.setNextAlarmLocked();
+ mSolvencyAlarmListener.setNextAlarmLocked();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.print("Current GDP: ");
+ pw.println(narcToString(mCurrentNarcsInCirculation));
+
+ pw.println();
+ mSolvencyAlarmListener.dumpLocked(pw);
+
+ pw.println();
+ mLedgerCleanupAlarmListener.dumpLocked(pw);
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
new file mode 100644
index 000000000000..acdf6898b3cc
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.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.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * AlarmManager.
+ */
+public class AlarmManagerEconomicPolicy extends EconomicPolicy {
+ public static final int ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 0;
+ public static final int ACTION_ALARM_WAKEUP_EXACT =
+ TYPE_ACTION | POLICY_AM | 1;
+ public static final int ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 2;
+ public static final int ACTION_ALARM_WAKEUP_INEXACT =
+ TYPE_ACTION | POLICY_AM | 3;
+ public static final int ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 4;
+ public static final int ACTION_ALARM_NONWAKEUP_EXACT =
+ TYPE_ACTION | POLICY_AM | 5;
+ public static final int ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 6;
+ public static final int ACTION_ALARM_NONWAKEUP_INEXACT =
+ TYPE_ACTION | POLICY_AM | 7;
+ public static final int ACTION_ALARM_CLOCK =
+ TYPE_ACTION | POLICY_AM | 8;
+
+ private static final int[] COST_MODIFIERS = new int[]{
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE
+ };
+
+ private final SparseArray<Action> mActions = new SparseArray<>();
+ private final SparseArray<Reward> mRewards = new SparseArray<>();
+
+ AlarmManagerEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ loadActions();
+ loadRewards();
+ }
+
+ @Override
+ long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ // TODO: take exemption into account
+ return arcToNarc(160);
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return arcToNarc(1440);
+ }
+
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return arcToNarc(52000);
+ }
+
+ @NonNull
+ @Override
+ int[] getCostModifiers() {
+ return COST_MODIFIERS;
+ }
+
+ @Nullable
+ @Override
+ Action getAction(@AppAction int actionId) {
+ return mActions.get(actionId);
+ }
+
+ @Nullable
+ @Override
+ Reward getReward(@UtilityReward int rewardId) {
+ return mRewards.get(rewardId);
+ }
+
+ private void loadActions() {
+ mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, arcToNarc(3), arcToNarc(5)));
+ mActions.put(ACTION_ALARM_WAKEUP_EXACT,
+ new Action(ACTION_ALARM_WAKEUP_EXACT, arcToNarc(3), arcToNarc(4)));
+ mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(3), arcToNarc(4)));
+ mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
+ new Action(ACTION_ALARM_WAKEUP_INEXACT, arcToNarc(3), arcToNarc(3)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(1), arcToNarc(3)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
+ new Action(ACTION_ALARM_NONWAKEUP_EXACT, arcToNarc(1), arcToNarc(2)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(1), arcToNarc(2)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
+ new Action(ACTION_ALARM_NONWAKEUP_INEXACT, arcToNarc(1), arcToNarc(1)));
+ mActions.put(ACTION_ALARM_CLOCK,
+ new Action(ACTION_ALARM_CLOCK, arcToNarc(5), arcToNarc(10)));
+ }
+
+ private void loadRewards() {
+ mRewards.put(REWARD_TOP_ACTIVITY,
+ new Reward(REWARD_TOP_ACTIVITY,
+ arcToNarc(0), /* .01 arcs */ arcToNarc(1) / 100, arcToNarc(500)));
+ mRewards.put(REWARD_NOTIFICATION_SEEN,
+ new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(3), arcToNarc(0), arcToNarc(60)));
+ mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+ new Reward(REWARD_NOTIFICATION_INTERACTION,
+ arcToNarc(5), arcToNarc(0), arcToNarc(500)));
+ mRewards.put(REWARD_WIDGET_INTERACTION,
+ new Reward(REWARD_WIDGET_INTERACTION, arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+ mRewards.put(REWARD_OTHER_USER_INTERACTION,
+ new Reward(REWARD_OTHER_USER_INTERACTION,
+ arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
new file mode 100644
index 000000000000..a627de691dab
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
@@ -0,0 +1,110 @@
+/*
+ * 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.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+
+/** Modifier that makes things free when the device is charging. */
+class ChargingModifier extends Modifier {
+ private static final String TAG = "TARE-" + ChargingModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternalResourceService mIrs;
+ private final ChargingTracker mChargingTracker;
+
+ ChargingModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mChargingTracker = new ChargingTracker();
+ mChargingTracker.startTracking(irs.getContext());
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ return modifyValue(ctp);
+ }
+
+ @Override
+ long getModifiedPrice(long price) {
+ return modifyValue(price);
+ }
+
+ private long modifyValue(long val) {
+ if (mChargingTracker.mCharging) {
+ return 0;
+ }
+ return val;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("charging=");
+ pw.println(mChargingTracker.mCharging);
+ }
+
+ private final class ChargingTracker extends BroadcastReceiver {
+ /**
+ * Track whether we're "charging", where charging means that we're ready to commit to
+ * doing work.
+ */
+ private volatile boolean mCharging;
+
+ public void startTracking(@NonNull Context context) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ mCharging = batteryManager.isCharging();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (BatteryManager.ACTION_CHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received charging intent, fired @ "
+ + SystemClock.elapsedRealtime());
+ }
+ if (!mCharging) {
+ mCharging = true;
+ mIrs.onDeviceStateChanged();
+ }
+ } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from power.");
+ }
+ if (mCharging) {
+ mCharging = false;
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
new file mode 100644
index 000000000000..8c56c6171508
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -0,0 +1,144 @@
+/*
+ * 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+
+/** Combines all enabled policies into one. */
+public class CompleteEconomicPolicy extends EconomicPolicy {
+ private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
+ /** Lazily populated set of actions covered by this policy. */
+ private final SparseArray<Action> mActions = new SparseArray<>();
+ /** Lazily populated set of rewards covered by this policy. */
+ private final SparseArray<Reward> mRewards = new SparseArray<>();
+ private final int[] mCostModifiers;
+ private final long mMaxSatiatedBalance;
+ private final long mMaxSatiatedCirculation;
+
+ CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
+ super(irs);
+ mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs));
+ mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs));
+
+ ArraySet<Integer> costModifiers = new ArraySet<>();
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers();
+ for (int s : sm) {
+ costModifiers.add(s);
+ }
+ }
+ mCostModifiers = new int[costModifiers.size()];
+ for (int i = 0; i < costModifiers.size(); ++i) {
+ mCostModifiers[i] = costModifiers.valueAt(i);
+ }
+
+ long max = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
+ }
+ mMaxSatiatedBalance = max;
+
+ max = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+ }
+ mMaxSatiatedCirculation = max;
+ }
+
+ @Override
+ public long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ long min = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
+ }
+ return min;
+ }
+
+ @Override
+ public long getMaxSatiatedBalance() {
+ return mMaxSatiatedBalance;
+ }
+
+ @Override
+ public long getMaxSatiatedCirculation() {
+ return mMaxSatiatedCirculation;
+ }
+
+ @NonNull
+ @Override
+ public int[] getCostModifiers() {
+ return mCostModifiers == null ? EmptyArray.INT : mCostModifiers;
+ }
+
+ @Nullable
+ @Override
+ public Action getAction(@AppAction int actionId) {
+ if (mActions.contains(actionId)) {
+ return mActions.get(actionId);
+ }
+
+ long ctp = 0, price = 0;
+ boolean exists = false;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionId);
+ if (a != null) {
+ exists = true;
+ ctp += a.costToProduce;
+ price += a.basePrice;
+ }
+ }
+ final Action action = exists ? new Action(actionId, ctp, price) : null;
+ mActions.put(actionId, action);
+ return action;
+ }
+
+ @Nullable
+ @Override
+ public Reward getReward(@UtilityReward int rewardId) {
+ if (mRewards.contains(rewardId)) {
+ return mRewards.get(rewardId);
+ }
+
+ long instantReward = 0, ongoingReward = 0, maxReward = 0;
+ boolean exists = false;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardId);
+ if (r != null) {
+ exists = true;
+ instantReward += r.instantReward;
+ ongoingReward += r.ongoingRewardPerSecond;
+ maxReward += r.maxDailyReward;
+ }
+ }
+ final Reward reward = exists
+ ? new Reward(rewardId, instantReward, ongoingReward, maxReward) : null;
+ mRewards.put(rewardId, reward);
+ return reward;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ dumpActiveModifiers(pw);
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
new file mode 100644
index 000000000000..20b9c70d9b97
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
@@ -0,0 +1,100 @@
+/*
+ * 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.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+/** Modifier that makes things more expensive in light and deep doze. */
+class DeviceIdleModifier extends Modifier {
+ private static final String TAG = "TARE-" + DeviceIdleModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternalResourceService mIrs;
+ private final PowerManager mPowerManager;
+ private final DeviceIdleTracker mDeviceIdleTracker;
+
+ DeviceIdleModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ mDeviceIdleTracker = new DeviceIdleTracker();
+ mDeviceIdleTracker.startTracking(irs.getContext());
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ if (mDeviceIdleTracker.mDeviceIdle) {
+ return (long) (1.2 * ctp);
+ }
+ if (mDeviceIdleTracker.mDeviceLightIdle) {
+ return (long) (1.1 * ctp);
+ }
+ return ctp;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("idle=");
+ pw.println(mDeviceIdleTracker.mDeviceIdle);
+ pw.print("lightIdle=");
+ pw.println(mDeviceIdleTracker.mDeviceLightIdle);
+ }
+
+ private final class DeviceIdleTracker extends BroadcastReceiver {
+
+ private volatile boolean mDeviceIdle;
+ private volatile boolean mDeviceLightIdle;
+
+ DeviceIdleTracker() {
+ }
+
+ void startTracking(@NonNull Context context) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ mDeviceIdle = mPowerManager.isDeviceIdleMode();
+ mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ if (mDeviceIdle != mPowerManager.isDeviceIdleMode()) {
+ mDeviceIdle = mPowerManager.isDeviceIdleMode();
+ mIrs.onDeviceStateChanged();
+ }
+ } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ if (mDeviceIdle != mPowerManager.isLightDeviceIdleMode()) {
+ mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
new file mode 100644
index 000000000000..0b5ff1b9e5d8
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -0,0 +1,389 @@
+/*
+ * 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.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An EconomicPolicy includes pricing information and daily ARC requirements and suggestions.
+ * Policies are defined per participating system service. This allows each service’s EconomicPolicy
+ * to be isolated while allowing the core economic system to scale across policies to achieve a
+ * logical system-wide value system.
+ */
+public abstract class EconomicPolicy {
+ private static final String TAG = "TARE-" + EconomicPolicy.class.getSimpleName();
+
+ private static final int SHIFT_TYPE = 30;
+ static final int MASK_TYPE = 0b11 << SHIFT_TYPE;
+ static final int TYPE_REGULATION = 0 << SHIFT_TYPE;
+ static final int TYPE_ACTION = 1 << SHIFT_TYPE;
+ static final int TYPE_REWARD = 2 << SHIFT_TYPE;
+
+ private static final int SHIFT_POLICY = 29;
+ static final int MASK_POLICY = 0b1 << SHIFT_POLICY;
+ static final int POLICY_AM = 0 << SHIFT_POLICY;
+ static final int POLICY_JS = 1 << SHIFT_POLICY;
+
+ static final int MASK_EVENT = ~0 - (0b111 << SHIFT_POLICY);
+
+ static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0;
+ static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1;
+ static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2;
+
+ static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0;
+ static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1;
+ static final int REWARD_TOP_ACTIVITY = TYPE_REWARD | 2;
+ static final int REWARD_WIDGET_INTERACTION = TYPE_REWARD | 3;
+ static final int REWARD_OTHER_USER_INTERACTION = TYPE_REWARD | 4;
+
+ @IntDef({
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppAction {
+ }
+
+ @IntDef({
+ TYPE_ACTION,
+ TYPE_REGULATION,
+ TYPE_REWARD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {
+ }
+
+ @IntDef({
+ REWARD_TOP_ACTIVITY,
+ REWARD_NOTIFICATION_SEEN,
+ REWARD_NOTIFICATION_INTERACTION,
+ REWARD_WIDGET_INTERACTION,
+ REWARD_OTHER_USER_INTERACTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtilityReward {
+ }
+
+ static class Action {
+ /** Unique id (including across policies) for this action. */
+ public final int id;
+ /**
+ * How many ARCs the system says it takes to perform this action.
+ */
+ public final long costToProduce;
+ /**
+ * The base price to perform this action. If this is
+ * less than the {@link #costToProduce}, then the system should not perform
+ * the action unless a modifier lowers the cost to produce.
+ */
+ public final long basePrice;
+
+ Action(int id, long costToProduce, long basePrice) {
+ this.id = id;
+ this.costToProduce = costToProduce;
+ this.basePrice = basePrice;
+ }
+ }
+
+ static class Reward {
+ /** Unique id (including across policies) for this reward. */
+ @UtilityReward
+ public final int id;
+ public final long instantReward;
+ /** Reward credited per second of ongoing activity. */
+ public final long ongoingRewardPerSecond;
+ /** The maximum amount an app can earn from this reward within a 24 hour period. */
+ public final long maxDailyReward;
+
+ Reward(int id, long instantReward, long ongoingReward, long maxDailyReward) {
+ this.id = id;
+ this.instantReward = instantReward;
+ this.ongoingRewardPerSecond = ongoingReward;
+ this.maxDailyReward = maxDailyReward;
+ }
+ }
+
+ private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
+
+ EconomicPolicy(@NonNull InternalResourceService irs) {
+ for (int mId : getCostModifiers()) {
+ initModifier(mId, irs);
+ }
+ }
+
+ @CallSuper
+ void onSystemServicesReady() {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.onSystemServicesReady();
+ }
+ }
+ }
+
+ /**
+ * Returns the minimum suggested balance an app should have when the device is at 100% battery.
+ * This takes into account any exemptions the app may have.
+ */
+ abstract long getMinSatiatedBalance(int userId, @NonNull String pkgName);
+
+ /**
+ * Returns the maximum balance an app should have when the device is at 100% battery. This
+ * exists to ensure that no single app accumulate all available resources and increases fairness
+ * for all apps.
+ */
+ abstract long getMaxSatiatedBalance();
+
+ /**
+ * Returns the maximum number of narcs that should be in circulation at once when the device is
+ * at 100% battery.
+ */
+ abstract long getMaxSatiatedCirculation();
+
+ /** Return the set of modifiers that should apply to this policy's costs. */
+ @NonNull
+ abstract int[] getCostModifiers();
+
+ @Nullable
+ abstract Action getAction(@AppAction int actionId);
+
+ @Nullable
+ abstract Reward getReward(@UtilityReward int rewardId);
+
+ void dump(IndentingPrintWriter pw) {
+ }
+
+ final long getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
+ final Action action = getAction(actionId);
+ if (action == null) {
+ return 0;
+ }
+ long ctp = action.costToProduce;
+ long price = action.basePrice;
+ final int[] costModifiers = getCostModifiers();
+ boolean useProcessStatePriceDeterminant = false;
+ for (int costModifier : costModifiers) {
+ if (costModifier == COST_MODIFIER_PROCESS_STATE) {
+ useProcessStatePriceDeterminant = true;
+ } else {
+ final Modifier modifier = getModifier(costModifier);
+ ctp = modifier.getModifiedCostToProduce(ctp);
+ price = modifier.getModifiedPrice(price);
+ }
+ }
+ // ProcessStateModifier needs to be done last.
+ if (useProcessStatePriceDeterminant) {
+ ProcessStateModifier processStateModifier =
+ (ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
+ price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
+ }
+ return price;
+ }
+
+ private static void initModifier(@Modifier.CostModifier final int modifierId,
+ @NonNull InternalResourceService irs) {
+ if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+ if (modifier == null) {
+ switch (modifierId) {
+ case COST_MODIFIER_CHARGING:
+ modifier = new ChargingModifier(irs);
+ break;
+ case COST_MODIFIER_DEVICE_IDLE:
+ modifier = new DeviceIdleModifier(irs);
+ break;
+ case COST_MODIFIER_POWER_SAVE_MODE:
+ modifier = new PowerSaveModeModifier(irs);
+ break;
+ case COST_MODIFIER_PROCESS_STATE:
+ modifier = new ProcessStateModifier(irs);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ COST_MODIFIER_BY_INDEX[modifierId] = modifier;
+ }
+ }
+
+ @NonNull
+ private static Modifier getModifier(@Modifier.CostModifier final int modifierId) {
+ if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+ if (modifier == null) {
+ throw new IllegalStateException(
+ "Modifier #" + modifierId + " was never initialized");
+ }
+ return modifier;
+ }
+
+ @EventType
+ static int getEventType(int eventId) {
+ return eventId & MASK_TYPE;
+ }
+
+ @NonNull
+ static String eventToString(int eventId) {
+ switch (eventId & MASK_TYPE) {
+ case TYPE_ACTION:
+ return actionToString(eventId);
+
+ case TYPE_REGULATION:
+ return regulationToString(eventId);
+
+ case TYPE_REWARD:
+ return rewardToString(eventId);
+
+ default:
+ return "UNKNOWN_EVENT:" + Integer.toHexString(eventId);
+ }
+ }
+
+ @NonNull
+ static String actionToString(int eventId) {
+ switch (eventId & MASK_POLICY) {
+ case POLICY_AM:
+ switch (eventId) {
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT:
+ return "ALARM_WAKEUP_EXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT:
+ return "ALARM_WAKEUP_INEXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT:
+ return "ALARM_NONWAKEUP_EXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT:
+ return "ALARM_NONWAKEUP_INEXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK:
+ return "ALARM_CLOCK";
+ }
+ case POLICY_JS:
+ switch (eventId) {
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START:
+ return "JOB_MAX_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING:
+ return "JOB_MAX_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START:
+ return "JOB_HIGH_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING:
+ return "JOB_HIGH_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START:
+ return "JOB_DEFAULT_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING:
+ return "JOB_DEFAULT_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START:
+ return "JOB_LOW_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING:
+ return "JOB_LOW_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START:
+ return "JOB_MIN_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING:
+ return "JOB_MIN_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT:
+ return "JOB_TIMEOUT";
+ }
+ }
+ return "UNKNOWN_ACTION:" + Integer.toHexString(eventId);
+ }
+
+ @NonNull
+ static String regulationToString(int eventId) {
+ switch (eventId) {
+ case REGULATION_BASIC_INCOME:
+ return "BASIC_INCOME";
+ case REGULATION_BIRTHRIGHT:
+ return "BIRTHRIGHT";
+ case REGULATION_WEALTH_RECLAMATION:
+ return "WEALTH_RECLAMATION";
+ }
+ return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId);
+ }
+
+ @NonNull
+ static String rewardToString(int eventId) {
+ switch (eventId) {
+ case REWARD_TOP_ACTIVITY:
+ return "REWARD_TOP_ACTIVITY";
+ case REWARD_NOTIFICATION_SEEN:
+ return "REWARD_NOTIFICATION_SEEN";
+ case REWARD_NOTIFICATION_INTERACTION:
+ return "REWARD_NOTIFICATION_INTERACTION";
+ case REWARD_WIDGET_INTERACTION:
+ return "REWARD_WIDGET_INTERACTION";
+ case REWARD_OTHER_USER_INTERACTION:
+ return "REWARD_OTHER_USER_INTERACTION";
+ }
+ return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
+ }
+
+ protected static void dumpActiveModifiers(IndentingPrintWriter pw) {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ pw.print("Modifier ");
+ pw.println(i);
+ pw.increaseIndent();
+
+ Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.dump(pw);
+ } else {
+ pw.println("NOT ACTIVE");
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
new file mode 100644
index 000000000000..5edeeedc3ff6
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Interface for the system server to deal with the resource economy subsystem.
+ *
+ * @hide
+ */
+interface EconomyManagerInternal {
+ /** Listener for when an app changes its solvency status. */
+ interface BalanceChangeListener {
+ /**
+ * Called when an app runs out of funds.
+ * {@link #noteOngoingEventStopped(int, String, int, String)} must still be called to
+ * formally end the action.
+ */
+ void onBankruptcy(int userId, @NonNull String pkgName);
+
+ /**
+ * Called when an app goes from being insolvent to solvent.
+ */
+ void onSolvent(int userId, @NonNull String pkgName);
+ }
+
+ /** Register a {@link BalanceChangeListener} to track all apps' solvency status changes. */
+ void registerBalanceChangeListener(@NonNull BalanceChangeListener listener);
+
+ /**
+ * Unregister a {@link BalanceChangeListener} from being notified of any app's solvency status
+ * changes.
+ */
+ void unregisterBalanceChangeListener(@NonNull BalanceChangeListener listener);
+
+ /**
+ * Return {@code true} if the app has a balance equal to or greater than the specified min
+ * balance.
+ */
+ boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, int minBalance);
+
+ /**
+ * Note that an instantaneous event has occurred. The event must be specified in one of the
+ * EconomicPolicies.
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag);
+
+ /**
+ * Note that a long-running event is starting. The event must be specified in one of the
+ * EconomicPolicies. You must always call
+ * {@link #noteOngoingEventStopped(int, String, int, String)} to end the event. Ongoing
+ * events will be separated and grouped by event-tag combinations. There must be an equal
+ * number of start() and stop() calls for the same event-tag combination in order for the
+ * tracking to finally stop (ie. ongoing events are ref-counted).
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag);
+
+ /**
+ * Note that a long-running event has stopped. The event must be specified in one of the
+ * EconomicPolicies. Ongoing events are separated and grouped by event-tag combinations.
+ * There must be an equal number of start() and stop() calls for the same event-tag combination
+ * in order for the tracking to finally stop (ie. ongoing events are ref-counted).
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
new file mode 100644
index 000000000000..4238ab318cbf
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -0,0 +1,469 @@
+/*
+ * 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseSetArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.tare.EconomyManagerInternal.BalanceChangeListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
+ * appropriate, and reclaiming ARCs at the right times. The IRS deals with the high level details
+ * while the {@link Agent} deals with the nitty-gritty details.
+ *
+ * Note on locking: Any function with the suffix 'Locked' needs to lock on {@link #mLock}.
+ *
+ * @hide
+ */
+public class InternalResourceService extends SystemService {
+ public static final String TAG = "TARE-IRS";
+ public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG);
+
+ static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS;
+ /** How much of an app's unused wealth should be reclaimed periodically. */
+ private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f;
+
+ /** Global local for all resource economy state. */
+ private final Object mLock = new Object();
+
+ private final Handler mHandler;
+ private final BatteryManagerInternal mBatteryManagerInternal;
+ private final PackageManager mPackageManager;
+
+ private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private final Agent mAgent;
+
+ private final CopyOnWriteArraySet<BalanceChangeListener> mBalanceChangeListeners =
+ new CopyOnWriteArraySet<>();
+
+ @NonNull
+ @GuardedBy("mLock")
+ private List<PackageInfo> mPkgCache = new ArrayList<>();
+
+ /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
+ @GuardedBy("mLock")
+ private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
+
+ @GuardedBy("mLock")
+ private boolean mIsSetup;
+ // In the range [0,100] to represent 0% to 100% battery.
+ @GuardedBy("mLock")
+ private int mCurrentBatteryLevel;
+ // TODO: load from disk
+ @GuardedBy("mLock")
+ private long mLastUnusedReclamationTime;
+
+ @SuppressWarnings("FieldCanBeLocal")
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Nullable
+ private String getPackageName(Intent intent) {
+ Uri uri = intent.getData();
+ return uri != null ? uri.getSchemeSpecificPart() : null;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_LEVEL_CHANGED:
+ onBatteryLevelChanged();
+ break;
+ case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ onPackageRemoved(pkgUid, pkgName);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_ADDED: {
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ onPackageAdded(pkgUid, pkgName);
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_RESTARTED: {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final int userId = UserHandle.getUserId(pkgUid);
+ onPackageForceStopped(userId, pkgName);
+ }
+ break;
+ case Intent.ACTION_USER_ADDED: {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ onUserAdded(userId);
+ }
+ break;
+ case Intent.ACTION_USER_REMOVED: {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ onUserRemoved(userId);
+ }
+ break;
+ }
+ }
+ };
+
+ private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener =
+ new AlarmManager.OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE);
+ mLastUnusedReclamationTime = System.currentTimeMillis();
+ scheduleUnusedWealthReclamationLocked();
+ }
+ }
+ };
+
+ private static final int MSG_NOTIFY_BALANCE_CHANGE_LISTENERS = 0;
+ private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
+ private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public InternalResourceService(Context context) {
+ super(context);
+
+ mHandler = new IrsHandler(TareHandlerThread.get().getLooper());
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ mPackageManager = context.getPackageManager();
+ mCompleteEconomicPolicy = new CompleteEconomicPolicy(this);
+ mAgent = new Agent(this, mCompleteEconomicPolicy);
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ final IntentFilter pkgFilter = new IntentFilter();
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ pkgFilter.addDataScheme("package");
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null);
+ final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+ publishLocalService(EconomyManagerInternal.class, new LocalService());
+ }
+
+ @Override
+ public void onStart() {
+
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (PHASE_SYSTEM_SERVICES_READY == phase) {
+ synchronized (mLock) {
+ mCurrentBatteryLevel = getCurrentBatteryLevel();
+ // TODO: base on if we have anything persisted
+ final boolean isFirstSetup = true;
+ if (isFirstSetup) {
+ mHandler.post(this::setupEconomy);
+ } else {
+ mIsSetup = true;
+ }
+ scheduleUnusedWealthReclamationLocked();
+ mCompleteEconomicPolicy.onSystemServicesReady();
+ }
+ }
+ }
+
+ @NonNull
+ Object getLock() {
+ return mLock;
+ }
+
+ @NonNull
+ List<PackageInfo> getInstalledPackages() {
+ synchronized (mLock) {
+ return mPkgCache;
+ }
+ }
+
+ @GuardedBy("mLock")
+ long getMaxCirculationLocked() {
+ return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+ }
+
+ @GuardedBy("mLock")
+ long getMinBalanceLocked(final int userId, @NonNull final String pkgName) {
+ return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName)
+ / 100;
+ }
+
+ void onBatteryLevelChanged() {
+ synchronized (mLock) {
+ final int newBatteryLevel = getCurrentBatteryLevel();
+ if (newBatteryLevel > mCurrentBatteryLevel) {
+ mAgent.distributeBasicIncomeLocked(newBatteryLevel);
+ }
+ mCurrentBatteryLevel = newBatteryLevel;
+ }
+ }
+
+ void onDeviceStateChanged() {
+ synchronized (mLock) {
+ mAgent.updateOngoingEventsLocked();
+ }
+ }
+
+ void onPackageAdded(final int uid, @NonNull final String pkgName) {
+ final int userId = UserHandle.getUserId(uid);
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = mPackageManager.getPackageInfoAsUser(pkgName, 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName);
+ return;
+ }
+ synchronized (mLock) {
+ mPkgCache.add(packageInfo);
+ mUidToPackageCache.add(uid, pkgName);
+ // TODO: only do this when the user first launches the app (app leaves stopped state)
+ mAgent.grantBirthrightLocked(userId, pkgName);
+ }
+ }
+
+ void onPackageForceStopped(final int userId, @NonNull final String pkgName) {
+ synchronized (mLock) {
+ // TODO: reduce ARC count by some amount
+ }
+ }
+
+ void onPackageRemoved(final int uid, @NonNull final String pkgName) {
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mLock) {
+ mUidToPackageCache.remove(uid, pkgName);
+ for (int i = 0; i < mPkgCache.size(); ++i) {
+ PackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
+ && pkgName.equals(pkgInfo.packageName)) {
+ mPkgCache.remove(i);
+ break;
+ }
+ }
+ mAgent.onPackageRemovedLocked(userId, pkgName);
+ }
+ }
+
+ void onUidStateChanged(final int uid) {
+ synchronized (mLock) {
+ final ArraySet<String> pkgNames = getPackagesForUidLocked(uid);
+ if (pkgNames == null) {
+ Slog.e(TAG, "Don't have packages for uid " + uid);
+ } else {
+ mAgent.updateOngoingEventsLocked(UserHandle.getUserId(uid), pkgNames);
+ }
+ }
+ }
+
+ void onUserAdded(final int userId) {
+ synchronized (mLock) {
+ loadInstalledPackageListLocked();
+ mAgent.grantBirthrightsLocked(userId);
+ }
+ }
+
+ void onUserRemoved(final int userId) {
+ synchronized (mLock) {
+ ArrayList<String> removedPkgs = new ArrayList<>();
+ for (int i = mPkgCache.size() - 1; i >= 0; --i) {
+ PackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) {
+ removedPkgs.add(pkgInfo.packageName);
+ mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
+ mPkgCache.remove(i);
+ break;
+ }
+ }
+ loadInstalledPackageListLocked();
+ mAgent.onUserRemovedLocked(userId, removedPkgs);
+ }
+ }
+
+ void postSolvencyChanged(final int userId, @NonNull final String pkgName, boolean nowSolvent) {
+ mHandler.obtainMessage(
+ MSG_NOTIFY_BALANCE_CHANGE_LISTENERS, userId, nowSolvent ? 1 : 0, pkgName)
+ .sendToTarget();
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleUnusedWealthReclamationLocked() {
+ final long now = System.currentTimeMillis();
+ final long nextReclamationTime =
+ Math.max(mLastUnusedReclamationTime + UNUSED_RECLAMATION_PERIOD_MS, now + 30_000);
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + (nextReclamationTime - now),
+ 30 * MINUTE_IN_MILLIS,
+ ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler);
+ } else {
+ mHandler.sendEmptyMessageDelayed(
+ MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, 30_000);
+ }
+ });
+ }
+
+ private int getCurrentBatteryLevel() {
+ return mBatteryManagerInternal.getBatteryLevel();
+ }
+
+ @Nullable
+ @GuardedBy("mLock")
+ private ArraySet<String> getPackagesForUidLocked(final int uid) {
+ ArraySet<String> packages = mUidToPackageCache.get(uid);
+ if (packages == null) {
+ final String[] pkgs = mPackageManager.getPackagesForUid(uid);
+ if (pkgs != null) {
+ for (String pkg : pkgs) {
+ mUidToPackageCache.add(uid, pkg);
+ }
+ packages = mUidToPackageCache.get(uid);
+ }
+ }
+ return packages;
+ }
+
+ @GuardedBy("mLock")
+ private void loadInstalledPackageListLocked() {
+ mPkgCache = mPackageManager.getInstalledPackages(0);
+ }
+
+ private void setupEconomy() {
+ synchronized (mLock) {
+ loadInstalledPackageListLocked();
+ mAgent.grantBirthrightsLocked();
+ mIsSetup = true;
+ }
+ }
+
+ private final class IrsHandler extends Handler {
+ IrsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_NOTIFY_BALANCE_CHANGE_LISTENERS:
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ final boolean nowSolvent = msg.arg2 == 1;
+ for (BalanceChangeListener listener : mBalanceChangeListeners) {
+ if (nowSolvent) {
+ listener.onSolvent(userId, pkgName);
+ } else {
+ listener.onBankruptcy(userId, pkgName);
+ }
+ }
+ break;
+
+ case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT:
+ removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT);
+ synchronized (mLock) {
+ scheduleUnusedWealthReclamationLocked();
+ }
+ break;
+ }
+ }
+ }
+
+ // TODO: implement
+ private final class LocalService implements EconomyManagerInternal {
+
+ @Override
+ public void registerBalanceChangeListener(@NonNull BalanceChangeListener listener) {
+ mBalanceChangeListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterBalanceChangeListener(@NonNull BalanceChangeListener listener) {
+ mBalanceChangeListeners.remove(listener);
+ }
+
+ @Override
+ public boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, int minBalance) {
+ return false;
+ }
+
+ @Override
+ public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag) {
+ synchronized (mLock) {
+ mAgent.noteInstantaneousEventLocked(userId, pkgName, eventId, tag);
+ }
+ }
+
+ @Override
+ public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag) {
+ synchronized (mLock) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ mAgent.noteOngoingEventLocked(userId, pkgName, eventId, tag, nowElapsed);
+ }
+ }
+
+ @Override
+ public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = System.currentTimeMillis();
+ synchronized (mLock) {
+ mAgent.stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now);
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
new file mode 100644
index 000000000000..cce75f7eb55b
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -0,0 +1,135 @@
+/*
+ * 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.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * JobScheduler.
+ */
+public class JobSchedulerEconomicPolicy extends EconomicPolicy {
+ public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JS | 0;
+ public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JS | 1;
+ public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JS | 2;
+ public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JS | 3;
+ public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JS | 4;
+ public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JS | 5;
+ public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JS | 6;
+ public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JS | 7;
+ public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JS | 8;
+ public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JS | 9;
+ public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JS | 10;
+
+ private static final int[] COST_MODIFIERS = new int[]{
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE
+ };
+
+ private final SparseArray<Action> mActions = new SparseArray<>();
+ private final SparseArray<Reward> mRewards = new SparseArray<>();
+
+ JobSchedulerEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ loadActions();
+ loadRewards();
+ }
+
+ @Override
+ long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ // TODO: incorporate time since usage
+ return arcToNarc(2000);
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return arcToNarc(60000);
+ }
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return arcToNarc(691200);
+ }
+
+ @NonNull
+ @Override
+ int[] getCostModifiers() {
+ return COST_MODIFIERS;
+ }
+
+ @Nullable
+ @Override
+ Action getAction(@AppAction int actionId) {
+ return mActions.get(actionId);
+ }
+
+ @Nullable
+ @Override
+ Reward getReward(@UtilityReward int rewardId) {
+ return mRewards.get(rewardId);
+ }
+
+ private void loadActions() {
+ mActions.put(ACTION_JOB_MAX_START,
+ new Action(ACTION_JOB_MAX_START, arcToNarc(3), arcToNarc(10)));
+ mActions.put(ACTION_JOB_MAX_RUNNING,
+ new Action(ACTION_JOB_MAX_RUNNING, arcToNarc(2), arcToNarc(5)));
+ mActions.put(ACTION_JOB_HIGH_START,
+ new Action(ACTION_JOB_HIGH_START, arcToNarc(3), arcToNarc(8)));
+ mActions.put(ACTION_JOB_HIGH_RUNNING,
+ new Action(ACTION_JOB_HIGH_RUNNING, arcToNarc(2), arcToNarc(4)));
+ mActions.put(ACTION_JOB_DEFAULT_START,
+ new Action(ACTION_JOB_DEFAULT_START, arcToNarc(3), arcToNarc(6)));
+ mActions.put(ACTION_JOB_DEFAULT_RUNNING,
+ new Action(ACTION_JOB_DEFAULT_RUNNING, arcToNarc(2), arcToNarc(3)));
+ mActions.put(ACTION_JOB_LOW_START,
+ new Action(ACTION_JOB_LOW_START, arcToNarc(3), arcToNarc(4)));
+ mActions.put(ACTION_JOB_LOW_RUNNING,
+ new Action(ACTION_JOB_LOW_RUNNING, arcToNarc(2), arcToNarc(2)));
+ mActions.put(ACTION_JOB_MIN_START,
+ new Action(ACTION_JOB_MIN_START, arcToNarc(3), arcToNarc(2)));
+ mActions.put(ACTION_JOB_MIN_RUNNING,
+ new Action(ACTION_JOB_MIN_RUNNING, arcToNarc(2), arcToNarc(1)));
+ mActions.put(ACTION_JOB_TIMEOUT,
+ new Action(ACTION_JOB_TIMEOUT, arcToNarc(30), arcToNarc(60)));
+ }
+
+ private void loadRewards() {
+ mRewards.put(REWARD_TOP_ACTIVITY,
+ new Reward(REWARD_TOP_ACTIVITY,
+ arcToNarc(0), /* .5 arcs */ arcToNarc(5) / 10, arcToNarc(15000)));
+ mRewards.put(REWARD_NOTIFICATION_SEEN,
+ new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(1), arcToNarc(0), arcToNarc(10)));
+ mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+ new Reward(REWARD_NOTIFICATION_INTERACTION,
+ arcToNarc(5), arcToNarc(0), arcToNarc(5000)));
+ mRewards.put(REWARD_WIDGET_INTERACTION,
+ new Reward(REWARD_WIDGET_INTERACTION,
+ arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+ mRewards.put(REWARD_OTHER_USER_INTERACTION,
+ new Reward(REWARD_OTHER_USER_INTERACTION,
+ arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
new file mode 100644
index 000000000000..ae1bf26f36f2
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.dumpTime;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+import android.util.SparseLongArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Ledger to track the last recorded balance and recent activities of an app.
+ */
+class Ledger {
+ static class Transaction {
+ public final long startTimeMs;
+ public final long endTimeMs;
+ public final int eventId;
+ @Nullable
+ public final String tag;
+ public final long delta;
+
+ Transaction(long startTimeMs, long endTimeMs,
+ int eventId, @Nullable String tag, long delta) {
+ this.startTimeMs = startTimeMs;
+ this.endTimeMs = endTimeMs;
+ this.eventId = eventId;
+ this.tag = tag;
+ this.delta = delta;
+ }
+ }
+
+ /** Last saved balance. This doesn't take currently ongoing events into account. */
+ private long mCurrentBalance = 0;
+ private final List<Transaction> mTransactions = new ArrayList<>();
+ private final SparseLongArray mCumulativeDeltaPerReason = new SparseLongArray();
+ private long mEarliestSumTime;
+
+ Ledger() {
+ }
+
+ long getCurrentBalance() {
+ return mCurrentBalance;
+ }
+
+ @Nullable
+ Transaction getEarliestTransaction() {
+ if (mTransactions.size() > 0) {
+ return mTransactions.get(0);
+ }
+ return null;
+ }
+
+ void recordTransaction(@NonNull Transaction transaction) {
+ mTransactions.add(transaction);
+ mCurrentBalance += transaction.delta;
+
+ final long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
+ mCumulativeDeltaPerReason.put(transaction.eventId, sum + transaction.delta);
+ mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs);
+ }
+
+ long get24HourSum(int eventId, final long now) {
+ final long windowStartTime = now - 24 * HOUR_IN_MILLIS;
+ if (mEarliestSumTime < windowStartTime) {
+ // Need to redo sums
+ mCumulativeDeltaPerReason.clear();
+ for (int i = mTransactions.size() - 1; i >= 0; --i) {
+ final Transaction transaction = mTransactions.get(i);
+ if (transaction.endTimeMs <= windowStartTime) {
+ break;
+ }
+ long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
+ if (transaction.startTimeMs >= windowStartTime) {
+ sum += transaction.delta;
+ } else {
+ // Pro-rate durationed deltas. Intentionally floor the result.
+ sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime)
+ * transaction.delta)
+ / (transaction.endTimeMs - transaction.startTimeMs);
+ }
+ mCumulativeDeltaPerReason.put(transaction.eventId, sum);
+ }
+ mEarliestSumTime = windowStartTime;
+ }
+ return mCumulativeDeltaPerReason.get(eventId);
+ }
+
+ /** Deletes transactions that are older than {@code minAgeMs}. */
+ void removeOldTransactions(long minAgeMs) {
+ final long cutoff = System.currentTimeMillis() - minAgeMs;
+ while (mTransactions.get(0).endTimeMs <= cutoff) {
+ mTransactions.remove(0);
+ }
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.print("Current balance", narcToString(getCurrentBalance())).println();
+ mTransactions.forEach((transaction) -> {
+ dumpTime(pw, transaction.startTimeMs);
+ pw.print("--");
+ dumpTime(pw, transaction.endTimeMs);
+ pw.print(": ");
+ pw.print(EconomicPolicy.eventToString(transaction.eventId));
+ pw.print(" --> ");
+ pw.println(narcToString(transaction.delta));
+ });
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
new file mode 100644
index 000000000000..6b89b796fae8
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
@@ -0,0 +1,67 @@
+/*
+ * 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.tare;
+
+import android.annotation.IntDef;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class of a modifier that can affect end pricing.
+ */
+abstract class Modifier {
+ static final int COST_MODIFIER_CHARGING = 0;
+ static final int COST_MODIFIER_DEVICE_IDLE = 1;
+ static final int COST_MODIFIER_POWER_SAVE_MODE = 2;
+ static final int COST_MODIFIER_PROCESS_STATE = 3;
+ static final int NUM_COST_MODIFIERS = COST_MODIFIER_PROCESS_STATE + 1;
+
+ @IntDef({
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CostModifier {
+ }
+
+ /**
+ * Returns a modified cost to produce based on the modifier's state.
+ *
+ * @param ctp Current cost to produce
+ */
+ long getModifiedCostToProduce(long ctp) {
+ return ctp;
+ }
+
+ /**
+ * Returns a modified price based on the modifier's state.
+ *
+ * @param price Current price
+ */
+ long getModifiedPrice(long price) {
+ return price;
+ }
+
+ void onSystemServicesReady() {
+ }
+
+ abstract void dump(IndentingPrintWriter pw);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/OWNERS b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
new file mode 100644
index 000000000000..96ec75f39e4e
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
@@ -0,0 +1,5 @@
+dplotnikov@google.com
+kwekua@google.com
+mwachens@google.com
+suprabh@google.com
+yamasani@google.com \ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
new file mode 100644
index 000000000000..764a3a8b1ed9
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tare;
+
+import android.annotation.NonNull;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+
+/** Modifier that makes things more expensive in adaptive and full battery saver are active. */
+class PowerSaveModeModifier extends Modifier {
+ private final InternalResourceService mIrs;
+ private final PowerManager mPowerManager;
+
+ PowerSaveModeModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ if (mPowerManager.isPowerSaveMode()) {
+ return (long) (1.5 * ctp);
+ }
+ // TODO: get adaptive power save mode
+ if (mPowerManager.isPowerSaveMode()) {
+ return (long) (1.25 * ctp);
+ }
+ return ctp;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("power save=");
+ pw.println(mPowerManager.isPowerSaveMode());
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
new file mode 100644
index 000000000000..2ee645990df5
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -0,0 +1,185 @@
+/*
+ * 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.tare;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.content.pm.PackageManagerInternal;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Modifier that makes things more cheaper based on an app's process state. */
+class ProcessStateModifier extends Modifier {
+ private static final String TAG = "TARE-" + ProcessStateModifier.class.getSimpleName();
+
+ private static final int PROC_STATE_BUCKET_NONE = 0;
+ private static final int PROC_STATE_BUCKET_TOP = 1;
+ private static final int PROC_STATE_BUCKET_FGS = 2;
+ private static final int PROC_STATE_BUCKET_BFGS = 3;
+ private static final int PROC_STATE_BUCKET_BG = 4;
+
+ @IntDef(prefix = {"PROC_STATE_BUCKET_"}, value = {
+ PROC_STATE_BUCKET_NONE,
+ PROC_STATE_BUCKET_TOP,
+ PROC_STATE_BUCKET_FGS,
+ PROC_STATE_BUCKET_BFGS,
+ PROC_STATE_BUCKET_BG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProcStateBucket {
+ }
+
+ private final Object mLock = new Object();
+ private final InternalResourceService mIrs;
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ /** Cached mapping of userId+package to their UIDs (for all users) */
+ private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
+
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+ final int newBucket = getProcStateBucket(procState);
+ synchronized (mLock) {
+ final int curBucket = mUidProcStateBucketCache.get(uid);
+ if (curBucket != newBucket) {
+ mUidProcStateBucketCache.put(uid, newBucket);
+ }
+ notifyStateChangedLocked(uid);
+ }
+ }
+
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ synchronized (mLock) {
+ if (mUidProcStateBucketCache.indexOfKey(uid) < 0) {
+ Slog.e(TAG, "UID " + uid + " marked gone but wasn't in cache.");
+ return;
+ }
+ mUidProcStateBucketCache.delete(uid);
+ notifyStateChangedLocked(uid);
+ }
+ }
+
+ @Override
+ public void onUidActive(int uid) {
+ }
+
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {
+ }
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
+ ProcessStateModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void onSystemServicesReady() {
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+ }
+
+ /**
+ * Get the final modified price based on an app's process state.
+ *
+ * @param ctp Cost to produce. @see EconomicPolicy.Action#costToProduce
+ * @param price Current price
+ */
+ long getModifiedPrice(final int userId, @NonNull final String pkgName,
+ final long ctp, final long price) {
+ final int procState;
+ synchronized (mLock) {
+ procState = mUidProcStateBucketCache.get(
+ getUidLocked(userId, pkgName), PROC_STATE_BUCKET_NONE);
+ }
+ switch (procState) {
+ case PROC_STATE_BUCKET_TOP:
+ return 0;
+ case PROC_STATE_BUCKET_FGS:
+ // Can't get notification priority. Just use CTP for now.
+ return ctp;
+ case PROC_STATE_BUCKET_BFGS:
+ return (long) (ctp + .5 * (price - ctp));
+ case PROC_STATE_BUCKET_BG:
+ default:
+ return price;
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void dump(IndentingPrintWriter pw) {
+ pw.print("Proc state bucket cache = ");
+ pw.println(mUidProcStateBucketCache);
+ }
+
+ @GuardedBy("mLock")
+ private int getUidLocked(final int userId, @NonNull final String pkgName) {
+ if (!mPackageToUidCache.contains(userId, pkgName)) {
+ mPackageToUidCache.add(userId, pkgName,
+ mPackageManagerInternal.getPackageUid(pkgName, 0, userId));
+ }
+ return mPackageToUidCache.get(userId, pkgName);
+ }
+
+ @ProcStateBucket
+ private int getProcStateBucket(int procState) {
+ if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+ return PROC_STATE_BUCKET_TOP;
+ }
+ if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ return PROC_STATE_BUCKET_FGS;
+ }
+ if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ return PROC_STATE_BUCKET_BFGS;
+ }
+ return PROC_STATE_BUCKET_BG;
+ }
+
+ @GuardedBy("mLock")
+ private void notifyStateChangedLocked(final int uid) {
+ // Never call out to the IRS with the local lock held.
+ TareHandlerThread.getHandler().post(() -> mIrs.onUidStateChanged(uid));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
new file mode 100644
index 000000000000..73b00b6cfa24
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"}
+ ]
+ }
+ ]
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
new file mode 100644
index 000000000000..5b2b6607ba14
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -0,0 +1,62 @@
+/*
+ * 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.tare;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
+
+/**
+ * Singleton thread for all of TARE.
+ *
+ * @see com.android.internal.os.BackgroundThread
+ */
+final class TareHandlerThread extends HandlerThread {
+
+ private static TareHandlerThread sInstance;
+ private static Handler sHandler;
+
+ private TareHandlerThread() {
+ super("tare");
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new TareHandlerThread();
+ sInstance.start();
+ final Looper looper = sInstance.getLooper();
+ looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ static TareHandlerThread get() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ }
+ return sInstance;
+ }
+
+ /** Returns the singleton handler for TareHandlerThread. */
+ public static Handler getHandler() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ }
+ return sHandler;
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
new file mode 100644
index 000000000000..2d72f5688688
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -0,0 +1,62 @@
+/*
+ * 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.util.IndentingPrintWriter;
+
+import java.text.SimpleDateFormat;
+
+class TareUtils {
+ private static final long NARC_IN_ARC = 1_000_000_000L;
+
+ @SuppressLint("SimpleDateFormat")
+ private static final SimpleDateFormat sDumpDateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
+ static long arcToNarc(int arcs) {
+ return arcs * NARC_IN_ARC;
+ }
+
+ static void dumpTime(IndentingPrintWriter pw, long time) {
+ pw.print(sDumpDateFormat.format(time));
+ }
+
+ static int narcToArc(long narcs) {
+ return (int) (narcs / NARC_IN_ARC);
+ }
+
+ @NonNull
+ static String narcToString(long narcs) {
+ if (narcs == 0) {
+ return "0 ARCs";
+ }
+ final long sub = Math.abs(narcs) % NARC_IN_ARC;
+ final long arcs = narcToArc(narcs);
+ if (arcs == 0) {
+ return sub + " narcs";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(arcs);
+ if (sub > 0) {
+ sb.append(".").append(sub / (NARC_IN_ARC / 1000));
+ }
+ sb.append(" ARCs");
+ return sb.toString();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index d532e20a0158..187422bf1970 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -462,6 +462,17 @@ public class AppIdleHistory {
return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
}
+ public long getTimeSinceLastUsedByUser(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, false);
+ if (appUsageHistory == null || appUsageHistory.lastUsedByUserElapsedTime == Long.MIN_VALUE
+ || appUsageHistory.lastUsedByUserElapsedTime == 0) {
+ return Long.MAX_VALUE;
+ }
+ return getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedByUserElapsedTime;
+ }
+
public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 4b081d258fd4..2fa10f08e9ae 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1082,6 +1082,14 @@ public class AppStandbyController
}
@Override
+ public long getTimeSinceLastUsedByUser(String packageName, int userId) {
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getTimeSinceLastUsedByUser(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
public void onUserRemoved(int userId) {
synchronized (mAppIdleLock) {
mAppIdleHistory.onUserRemoved(userId);
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
index 70c908787472..3d2191410413 100644
--- a/apex/media/framework/TEST_MAPPING
+++ b/apex/media/framework/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsMediaParserTestCases"
},
{
- "name": "CtsMediaParserHostSideTestCases"
+ "name": "CtsMediaParserHostTestCases"
}
]
}
diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
index 26f4568fa7e5..7e71591d05fb 100644
--- a/apex/media/framework/java/android/media/Session2Command.java
+++ b/apex/media/framework/java/android/media/Session2Command.java
@@ -37,9 +37,8 @@ import java.util.Objects;
* If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
* {@link #getCustomAction()} shouldn't be {@code null}.
* <p>
- * Refer to the
- * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a>
- * class for the list of valid commands.
+ * Refer to the <a href="{@docRoot}reference/androidx/media2/session/SessionCommand.html">
+ * AndroidX SessionCommand</a> class for the list of valid commands.
*/
public final class Session2Command implements Parcelable {
/**
diff --git a/boot/Android.bp b/boot/Android.bp
index e8d88a531b5b..b71f9bf519cb 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -76,6 +76,10 @@ platform_bootclasspath {
module: "com.android.mediaprovider-bootclasspath-fragment",
},
{
+ apex: "com.android.nearby",
+ module: "com.android.nearby-bootclasspath-fragment",
+ },
+ {
apex: "com.android.os.statsd",
module: "com.android.os.statsd-bootclasspath-fragment",
},
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index b2b66c27f795..3534624a58a2 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -71,7 +71,7 @@ cc_library_shared {
"libui",
"libjnigraphics",
"libEGL",
- "libGLESv1_CM",
+ "libGLESv2",
"libgui",
],
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3109c5c1e075..25b7f249fc49 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -52,9 +52,8 @@
#include <gui/DisplayEventReceiver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
#include <EGL/eglext.h>
#include "BootAnimation.h"
@@ -108,6 +107,53 @@ static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
+static const char U_TEXTURE[] = "uTexture";
+static const char U_FADE[] = "uFade";
+static const char U_CROP_AREA[] = "uCropArea";
+static const char A_UV[] = "aUv";
+static const char A_POSITION[] = "aPosition";
+static const char VERTEX_SHADER_SOURCE[] = R"(
+ attribute vec4 aPosition;
+ attribute vec2 aUv;
+ varying vec2 vUv;
+ void main() {
+ gl_Position = aPosition;
+ vUv = aUv;
+ })";
+static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
+ uniform sampler2D uTexture;
+ uniform float uFade;
+ varying vec2 vUv;
+ void main() {
+ vec4 color = texture2D(uTexture, vUv);
+ gl_FragColor = vec4(color.x, color.y, color.z, 1.0 - uFade);
+ })";
+static const char TEXT_FRAG_SHADER_SOURCE[] = R"(
+ uniform sampler2D uTexture;
+ uniform vec4 uCropArea;
+ varying vec2 vUv;
+ void main() {
+ vec2 uv = vec2(mix(uCropArea.x, uCropArea.z, vUv.x),
+ mix(uCropArea.y, uCropArea.w, vUv.y));
+ gl_FragColor = texture2D(uTexture, uv);
+ })";
+
+static GLfloat quadPositions[] = {
+ -0.5f, -0.5f,
+ +0.5f, -0.5f,
+ +0.5f, +0.5f,
+ +0.5f, +0.5f,
+ -0.5f, +0.5f,
+ -0.5f, -0.5f
+};
+static GLfloat quadUVs[] = {
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 0.0f,
+ 0.0f, 1.0f
+};
// ---------------------------------------------------------------------------
@@ -209,7 +255,6 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
const int w = bitmapInfo.width;
const int h = bitmapInfo.height;
- GLint crop[4] = { 0, h, w, -h };
texture->w = w;
texture->h = h;
@@ -237,11 +282,10 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
break;
}
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return NO_ERROR;
}
@@ -263,7 +307,6 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
const int w = bitmapInfo.width;
const int h = bitmapInfo.height;
- GLint crop[4] = { 0, h, w, -h };
int tw = 1 << (31 - __builtin_clz(w));
int th = 1 << (31 - __builtin_clz(h));
if (tw < w) tw <<= 1;
@@ -297,7 +340,10 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
break;
}
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
*width = w;
*height = h;
@@ -470,7 +516,9 @@ status_t BootAnimation::readyToRun() {
eglInitialize(display, nullptr, nullptr);
EGLConfig config = getEglConfig(display);
EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
- EGLContext context = eglCreateContext(display, config, nullptr, nullptr);
+ // Initialize egl context with client version number 2.0.
+ EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes);
EGLint w, h;
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
@@ -503,11 +551,6 @@ status_t BootAnimation::readyToRun() {
void BootAnimation::projectSceneToWindow() {
glViewport(0, 0, mWidth, mHeight);
glScissor(0, 0, mWidth, mHeight);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
}
void BootAnimation::resizeSurface(int newWidth, int newHeight) {
@@ -600,8 +643,68 @@ void BootAnimation::findBootAnimationFile() {
}
}
+GLuint compileShader(GLenum shaderType, const GLchar *source) {
+ GLuint shader = glCreateShader(shaderType);
+ glShaderSource(shader, 1, &source, 0);
+ glCompileShader(shader);
+ GLint isCompiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+ if (isCompiled == GL_FALSE) {
+ SLOGE("Compile shader failed. Shader type: %d", shaderType);
+ return 0;
+ }
+ return shader;
+}
+
+GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) {
+ GLuint program = glCreateProgram();
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ GLint isLinked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked);
+ if (isLinked == GL_FALSE) {
+ SLOGE("Linking shader failed. Shader handles: vert %d, frag %d",
+ vertexShader, fragmentShader);
+ return 0;
+ }
+ return program;
+}
+
+void BootAnimation::initShaders() {
+ GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE);
+ GLuint imageFragmentShader =
+ compileShader(GL_FRAGMENT_SHADER, (const GLchar *)IMAGE_FRAG_SHADER_SOURCE);
+ GLuint textFragmentShader =
+ compileShader(GL_FRAGMENT_SHADER, (const GLchar *)TEXT_FRAG_SHADER_SOURCE);
+
+ // Initialize image shader.
+ mImageShader = linkShader(vertexShader, imageFragmentShader);
+ GLint positionLocation = glGetAttribLocation(mImageShader, A_POSITION);
+ GLint uvLocation = glGetAttribLocation(mImageShader, A_UV);
+ mImageTextureLocation = glGetUniformLocation(mImageShader, U_TEXTURE);
+ mImageFadeLocation = glGetUniformLocation(mImageShader, U_FADE);
+ glEnableVertexAttribArray(positionLocation);
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, quadPositions);
+ glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
+ glEnableVertexAttribArray(uvLocation);
+
+ // Initialize text shader.
+ mTextShader = linkShader(vertexShader, textFragmentShader);
+ positionLocation = glGetAttribLocation(mTextShader, A_POSITION);
+ uvLocation = glGetAttribLocation(mTextShader, A_UV);
+ mTextTextureLocation = glGetUniformLocation(mTextShader, U_TEXTURE);
+ mTextCropAreaLocation = glGetUniformLocation(mTextShader, U_CROP_AREA);
+ glEnableVertexAttribArray(positionLocation);
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, quadPositions);
+ glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
+ glEnableVertexAttribArray(uvLocation);
+}
+
bool BootAnimation::threadLoop() {
bool result;
+ initShaders();
+
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
@@ -623,6 +726,8 @@ bool BootAnimation::threadLoop() {
}
bool BootAnimation::android() {
+ glActiveTexture(GL_TEXTURE0);
+
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
@@ -631,19 +736,14 @@ bool BootAnimation::android() {
mCallbacks->init({});
// clear screen
- glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(mDisplay, mSurface);
- glEnable(GL_TEXTURE_2D);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-
// Blend state
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
const nsecs_t startTime = systemTime();
do {
@@ -666,12 +766,12 @@ bool BootAnimation::android() {
glEnable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
- glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);
- glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
+ drawTexturedQuad(x, yc, mAndroid[1].w, mAndroid[1].h);
+ drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
- glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
+ drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h);
EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
if (res == EGL_FALSE)
@@ -798,10 +898,10 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
status = initTexture(font->map, &font->texture.w, &font->texture.h);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else if (fallback != nullptr) {
status = initTexture(&font->texture, mAssets, fallback);
} else {
@@ -816,40 +916,11 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
return status;
}
-void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth,
- const int frameHeight, const Animation::Part& part,
- const int fadedFramesCount) {
- glEnable(GL_BLEND);
- glEnableClientState(GL_VERTEX_ARRAY);
- glDisable(GL_TEXTURE_2D);
- // avoid creating a hole due to mixing result alpha with GL_REPLACE texture
- glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
-
- const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount;
- glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha);
-
- const float frameStartX = static_cast<float>(frameLeft);
- const float frameStartY = static_cast<float>(frameBottom);
- const float frameEndX = frameStartX + frameWidth;
- const float frameEndY = frameStartY + frameHeight;
- const GLfloat frameRect[] = {
- frameStartX, frameStartY,
- frameEndX, frameStartY,
- frameEndX, frameEndY,
- frameStartX, frameEndY
- };
- glVertexPointer(2, GL_FLOAT, 0, frameRect);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_TEXTURE_2D);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisable(GL_BLEND);
-}
-
void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
glEnable(GL_BLEND); // Allow us to draw on top of the animation
glBindTexture(GL_TEXTURE_2D, font.texture.name);
+ glUseProgram(mTextShader);
+ glUniform1i(mTextTextureLocation, 0);
const int len = strlen(str);
const int strWidth = font.char_width * len;
@@ -865,8 +936,6 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int*
*y = mHeight + *y - font.char_height;
}
- int cropRect[4] = { 0, 0, font.char_width, -font.char_height };
-
for (int i = 0; i < len; i++) {
char c = str[i];
@@ -878,13 +947,13 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int*
const int charPos = (c - FONT_BEGIN_CHAR); // Position in the list of valid characters
const int row = charPos / FONT_NUM_COLS;
const int col = charPos % FONT_NUM_COLS;
- cropRect[0] = col * font.char_width; // Left of column
- cropRect[1] = row * font.char_height * 2; // Top of row
- // Move down to bottom of regular (one char_heigh) or bold (two char_heigh) line
- cropRect[1] += bold ? 2 * font.char_height : font.char_height;
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
-
- glDrawTexiOES(*x, *y, 0, font.char_width, font.char_height);
+ // Bold fonts are expected in the second half of each row.
+ float v0 = (row + (bold ? 0.5f : 0.0f)) / FONT_NUM_ROWS;
+ float u0 = ((float)col) / FONT_NUM_COLS;
+ float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2;
+ float u1 = u0 + 1.0f / FONT_NUM_COLS;
+ glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1);
+ drawTexturedQuad(*x, *y, font.char_width, font.char_height);
*x += font.char_width;
}
@@ -1166,19 +1235,16 @@ bool BootAnimation::movie() {
// Blend required to draw time on top of animation frames.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool clockFontInitialized = false;
if (mClockEnabled) {
clockFontInitialized =
@@ -1218,6 +1284,34 @@ bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part,
(lastDisplayedProgress == 0 || lastDisplayedProgress == 100);
}
+// Linear mapping from range <a1, a2> to range <b1, b2>
+float mapLinear(float x, float a1, float a2, float b1, float b2) {
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+}
+
+void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) {
+ // Map coordinates from screen space to world space.
+ float x0 = mapLinear(xStart, 0, mWidth, -1, 1);
+ float y0 = mapLinear(yStart, 0, mHeight, -1, 1);
+ float x1 = mapLinear(xStart + width, 0, mWidth, -1, 1);
+ float y1 = mapLinear(yStart + height, 0, mHeight, -1, 1);
+ // Update quad vertex positions.
+ quadPositions[0] = x0;
+ quadPositions[1] = y0;
+ quadPositions[2] = x1;
+ quadPositions[3] = y0;
+ quadPositions[4] = x1;
+ quadPositions[5] = y1;
+ quadPositions[6] = x1;
+ quadPositions[7] = y1;
+ quadPositions[8] = x0;
+ quadPositions[9] = y1;
+ quadPositions[10] = x0;
+ quadPositions[11] = y0;
+ glDrawArrays(GL_TRIANGLES, 0,
+ sizeof(quadPositions) / sizeof(quadPositions[0]) / 2);
+}
+
bool BootAnimation::playAnimation(const Animation& animation) {
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -1230,7 +1324,6 @@ bool BootAnimation::playAnimation(const Animation& animation) {
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
- glBindTexture(GL_TEXTURE_2D, 0);
// Handle animation package
if (part.animation != nullptr) {
@@ -1272,12 +1365,8 @@ bool BootAnimation::playAnimation(const Animation& animation) {
if (r > 0) {
glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
- if (part.count != 1) {
- glGenTextures(1, &frame.tid);
- glBindTexture(GL_TEXTURE_2D, frame.tid);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- }
+ glGenTextures(1, &frame.tid);
+ glBindTexture(GL_TEXTURE_2D, frame.tid);
int w, h;
initTexture(frame.map, &w, &h);
}
@@ -1300,16 +1389,21 @@ bool BootAnimation::playAnimation(const Animation& animation) {
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
// which is equivalent to mHeight - (yc + frame.trimHeight)
const int frameDrawY = mHeight - (yc + frame.trimHeight);
- glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);
+ float fade = 0;
// if the part hasn't been stopped yet then continue fading if necessary
if (exitPending() && part.hasFadingPhase()) {
- fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
- ++fadedFramesCount);
+ fade = static_cast<float>(++fadedFramesCount) / part.framesToFadeCount;
if (fadedFramesCount >= part.framesToFadeCount) {
fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
}
}
+ glUseProgram(mImageShader);
+ glUniform1i(mImageTextureLocation, 0);
+ glUniform1f(mImageFadeLocation, fade);
+ glEnable(GL_BLEND);
+ drawTexturedQuad(xc, frameDrawY, frame.trimWidth, frame.trimHeight);
+ glDisable(GL_BLEND);
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index f8a31c6d8790..7b616d91c58b 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -31,7 +31,7 @@
#include <binder/IBinder.h>
#include <EGL/egl.h>
-#include <GLES/gl.h>
+#include <GLES2/gl2.h>
namespace android {
@@ -166,6 +166,7 @@ private:
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(FileMap* map, int* width, int* height);
status_t initFont(Font* font, const char* fallback);
+ void initShaders();
bool android();
bool movie();
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
@@ -173,6 +174,7 @@ private:
void drawProgress(int percent, const Font& font, const int xPos, const int yPos);
void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
const Animation::Part& part, int fadedFramesCount);
+ void drawTexturedQuad(float xStart, float yStart, float width, float height);
bool validClock(const Animation::Part& part);
Animation* loadAnimation(const String8&);
bool playAnimation(const Animation&);
@@ -218,6 +220,12 @@ private:
sp<TimeCheckThread> mTimeCheckThread = nullptr;
sp<Callbacks> mCallbacks;
Animation* mAnimation = nullptr;
+ GLuint mImageShader;
+ GLuint mTextShader;
+ GLuint mImageFadeLocation;
+ GLuint mImageTextureLocation;
+ GLuint mTextCropAreaLocation;
+ GLuint mTextTextureLocation;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d4da5e554591..7e6a521ca46c 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -45,13 +45,13 @@ using namespace android;
#define COLORSPACE_SRGB 1
#define COLORSPACE_DISPLAY_P3 2
-static void usage(const char* pname, PhysicalDisplayId displayId)
+static void usage(const char* pname, DisplayId displayId)
{
fprintf(stderr,
"usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
" -p: save the file as a png.\n"
- " -d: specify the physical display ID to capture (default: %s)\n"
+ " -d: specify the display ID to capture (default: %s)\n"
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
@@ -121,9 +121,9 @@ static status_t notifyMediaScanner(const char* fileName) {
int main(int argc, char** argv)
{
- std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
+ std::optional<DisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
if (!displayId) {
- fprintf(stderr, "Failed to get token for internal display\n");
+ fprintf(stderr, "Failed to get ID for internal display\n");
return 1;
}
@@ -136,7 +136,11 @@ int main(int argc, char** argv)
png = true;
break;
case 'd':
- displayId = PhysicalDisplayId(atoll(optarg));
+ displayId = DisplayId::fromValue(atoll(optarg));
+ if (!displayId) {
+ fprintf(stderr, "Invalid display ID\n");
+ return 1;
+ }
break;
case '?':
case 'h':
@@ -182,7 +186,7 @@ int main(int argc, char** argv)
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
+ status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
diff --git a/config/OWNERS b/config/OWNERS
index 0691dbc4dcb0..c0778f88637f 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,8 +1,8 @@
include /ZYGOTE_OWNERS
# art-team@ manages the boot image profiles
-per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file boot-* = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file dirty-image-objects = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file generate-preloaded-classes.sh = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file preloaded-classes* = calin@google.com, ngeoffray@google.com, vmarko@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 1de47b548a5c..894a894967cc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7455,7 +7455,7 @@ package android.app.admin {
field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
field public static final int KEYGUARD_DISABLE_IRIS = 256; // 0x100
- field public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
+ field @Deprecated public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
field public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4; // 0x4
field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10
@@ -7937,6 +7937,7 @@ package android.app.job {
method public static final long getMinFlexMillis();
method public long getMinLatencyMillis();
method public static final long getMinPeriodMillis();
+ method public long getMinimumNetworkChunkBytes();
method @Deprecated public int getNetworkType();
method @Nullable public android.net.NetworkRequest getRequiredNetwork();
method @NonNull public android.content.ComponentName getService();
@@ -7979,6 +7980,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
+ method @NonNull public android.app.job.JobInfo.Builder setMinimumNetworkChunkBytes(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
@@ -8071,11 +8073,13 @@ package android.app.job {
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.content.Intent, long, long);
+ ctor public JobWorkItem(@Nullable android.content.Intent, long, long, long);
method public int describeContents();
method public int getDeliveryCount();
method public long getEstimatedNetworkDownloadBytes();
method public long getEstimatedNetworkUploadBytes();
method public android.content.Intent getIntent();
+ method public long getMinimumNetworkChunkBytes();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
}
@@ -9429,6 +9433,7 @@ package android.bluetooth {
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull android.bluetooth.BluetoothDevice);
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
}
@@ -9453,6 +9458,7 @@ package android.bluetooth {
field @Deprecated public static final int HEALTH = 3; // 0x3
field public static final int HEARING_AID = 21; // 0x15
field public static final int HID_DEVICE = 19; // 0x13
+ field public static final int LE_AUDIO = 22; // 0x16
field public static final int SAP = 10; // 0xa
field public static final int STATE_CONNECTED = 2; // 0x2
field public static final int STATE_CONNECTING = 1; // 0x1
@@ -11175,11 +11181,13 @@ package android.content {
field public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
field public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
field public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+ field public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
field public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
field public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
field public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
field public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
field public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
+ field public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
field public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
field public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
field public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
@@ -11372,6 +11380,8 @@ package android.content {
method public final void addDataScheme(String);
method public final void addDataSchemeSpecificPart(String, int);
method public final void addDataType(String) throws android.content.IntentFilter.MalformedMimeTypeException;
+ method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicate();
+ method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicateWithTypeResolution(@NonNull android.content.ContentResolver);
method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator();
method public final java.util.Iterator<java.lang.String> categoriesIterator();
method public final int countActions();
@@ -12545,6 +12555,7 @@ package android.content.pm {
method public abstract int getInstantAppCookieMaxBytes();
method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String);
+ method @NonNull public android.content.IntentSender getLaunchIntentSenderForPackage(@NonNull String);
method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String);
method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -15254,9 +15265,11 @@ package android.graphics {
method public void clearContent();
method @NonNull public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
method public void destroy();
+ method public static boolean isDrawingEnabled();
method public boolean isOpaque();
method public void notifyFramePending();
method public void setContentRoot(@Nullable android.graphics.RenderNode);
+ method public static void setDrawingEnabled(boolean);
method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setLightSourceGeometry(float, float, float, float);
method public void setName(@NonNull String);
@@ -20336,24 +20349,45 @@ package android.media {
field public static final int CHANNEL_IN_Y_AXIS = 4096; // 0x1000
field public static final int CHANNEL_IN_Z_AXIS = 8192; // 0x2000
field public static final int CHANNEL_OUT_5POINT1 = 252; // 0xfc
+ field public static final int CHANNEL_OUT_5POINT1POINT2 = 3145980; // 0x3000fc
+ field public static final int CHANNEL_OUT_5POINT1POINT4 = 737532; // 0xb40fc
field @Deprecated public static final int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+ field public static final int CHANNEL_OUT_7POINT1POINT2 = 3152124; // 0x3018fc
+ field public static final int CHANNEL_OUT_7POINT1POINT4 = 743676; // 0xb58fc
field public static final int CHANNEL_OUT_7POINT1_SURROUND = 6396; // 0x18fc
+ field public static final int CHANNEL_OUT_9POINT1POINT4 = 202070268; // 0xc0b58fc
+ field public static final int CHANNEL_OUT_9POINT1POINT6 = 205215996; // 0xc3b58fc
field public static final int CHANNEL_OUT_BACK_CENTER = 1024; // 0x400
field public static final int CHANNEL_OUT_BACK_LEFT = 64; // 0x40
field public static final int CHANNEL_OUT_BACK_RIGHT = 128; // 0x80
+ field public static final int CHANNEL_OUT_BOTTOM_FRONT_CENTER = 8388608; // 0x800000
+ field public static final int CHANNEL_OUT_BOTTOM_FRONT_LEFT = 4194304; // 0x400000
+ field public static final int CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 16777216; // 0x1000000
field public static final int CHANNEL_OUT_DEFAULT = 1; // 0x1
field public static final int CHANNEL_OUT_FRONT_CENTER = 16; // 0x10
field public static final int CHANNEL_OUT_FRONT_LEFT = 4; // 0x4
field public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 256; // 0x100
field public static final int CHANNEL_OUT_FRONT_RIGHT = 8; // 0x8
field public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 512; // 0x200
+ field public static final int CHANNEL_OUT_FRONT_WIDE_LEFT = 67108864; // 0x4000000
+ field public static final int CHANNEL_OUT_FRONT_WIDE_RIGHT = 134217728; // 0x8000000
field public static final int CHANNEL_OUT_LOW_FREQUENCY = 32; // 0x20
+ field public static final int CHANNEL_OUT_LOW_FREQUENCY_2 = 33554432; // 0x2000000
field public static final int CHANNEL_OUT_MONO = 4; // 0x4
field public static final int CHANNEL_OUT_QUAD = 204; // 0xcc
field public static final int CHANNEL_OUT_SIDE_LEFT = 2048; // 0x800
field public static final int CHANNEL_OUT_SIDE_RIGHT = 4096; // 0x1000
field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
+ field public static final int CHANNEL_OUT_TOP_BACK_CENTER = 262144; // 0x40000
+ field public static final int CHANNEL_OUT_TOP_BACK_LEFT = 131072; // 0x20000
+ field public static final int CHANNEL_OUT_TOP_BACK_RIGHT = 524288; // 0x80000
+ field public static final int CHANNEL_OUT_TOP_CENTER = 8192; // 0x2000
+ field public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 32768; // 0x8000
+ field public static final int CHANNEL_OUT_TOP_FRONT_LEFT = 16384; // 0x4000
+ field public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 65536; // 0x10000
+ field public static final int CHANNEL_OUT_TOP_SIDE_LEFT = 1048576; // 0x100000
+ field public static final int CHANNEL_OUT_TOP_SIDE_RIGHT = 2097152; // 0x200000
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
field public static final int ENCODING_AAC_ELD = 15; // 0xf
field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
@@ -30797,6 +30831,7 @@ package android.os {
field public static final int Q = 29; // 0x1d
field public static final int R = 30; // 0x1e
field public static final int S = 31; // 0x1f
+ field public static final int T = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -31070,8 +31105,8 @@ package android.os {
ctor public Environment();
method public static java.io.File getDataDirectory();
method public static java.io.File getDownloadCacheDirectory();
- method @Deprecated public static java.io.File getExternalStorageDirectory();
- method @Deprecated public static java.io.File getExternalStoragePublicDirectory(String);
+ method public static java.io.File getExternalStorageDirectory();
+ method public static java.io.File getExternalStoragePublicDirectory(String);
method public static String getExternalStorageState();
method public static String getExternalStorageState(java.io.File);
method @NonNull public static java.io.File getRootDirectory();
@@ -51019,6 +51054,7 @@ package android.view.accessibility {
method public CharSequence getClassName();
method public CharSequence getContentDescription();
method public int getCurrentItemIndex();
+ method public int getDisplayId();
method public int getFromIndex();
method public int getItemCount();
method public int getMaxScrollX();
@@ -51435,6 +51471,7 @@ package android.view.autofill {
public final class AutofillManager {
method public void cancel();
+ method public void clearAutofillRequestCallback();
method public void commit();
method public void disableAutofillServices();
method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -51460,6 +51497,7 @@ package android.view.autofill {
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
+ method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
@@ -51478,6 +51516,10 @@ package android.view.autofill {
field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
}
+ public interface AutofillRequestCallback {
+ method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
+ }
+
public final class AutofillValue implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutofillValue forDate(long);
@@ -51848,10 +51890,12 @@ package android.view.inputmethod {
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e48a1da7b6a7..ef9df6609bb9 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -102,6 +102,14 @@ package android.hardware.usb {
}
+package android.location {
+
+ public class LocationManager {
+ method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull android.location.Location);
+ }
+
+}
+
package android.media {
public class AudioManager {
@@ -266,6 +274,10 @@ package android.os {
field public static final int VPN_UID = 1016; // 0x3f8
}
+ public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
+ method @NonNull public static android.os.SharedMemory create(@NonNull android.os.ParcelFileDescriptor);
+ }
+
public class StatsServiceManager {
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2d73aa67ed1a..0d15a03824ca 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2087,6 +2087,7 @@ package android.bluetooth {
field public static final int PBAP_CLIENT = 17; // 0x11
field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0
field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
+ field public static final int VOLUME_CONTROL = 23; // 0x17
}
public final class BluetoothStatusCodes {
@@ -2127,6 +2128,16 @@ package android.bluetooth {
field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
}
+ public final class BluetoothVolumeControl implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(@Nullable android.bluetooth.BluetoothDevice, @IntRange(from=0, to=255) int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BufferConstraint implements android.os.Parcelable {
ctor public BufferConstraint(int, int, int);
method public int describeContents();
@@ -2202,17 +2213,17 @@ package android.bluetooth.le {
public final class BluetoothLeScanner {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
}
- public final class ResultStorageDescriptor implements android.os.Parcelable {
- ctor public ResultStorageDescriptor(int, int, int);
- method public int describeContents();
- method public int getLength();
- method public int getOffset();
- method public int getType();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
+ @Deprecated public final class ResultStorageDescriptor implements android.os.Parcelable {
+ ctor @Deprecated public ResultStorageDescriptor(int, int, int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public int getLength();
+ method @Deprecated public int getOffset();
+ method @Deprecated public int getType();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
}
public final class ScanFilter implements android.os.Parcelable {
@@ -2236,10 +2247,10 @@ package android.bluetooth.le {
method public android.bluetooth.le.ScanSettings.Builder setScanResultType(int);
}
- public final class TruncatedFilter {
- ctor public TruncatedFilter(android.bluetooth.le.ScanFilter, java.util.List<android.bluetooth.le.ResultStorageDescriptor>);
- method public android.bluetooth.le.ScanFilter getFilter();
- method public java.util.List<android.bluetooth.le.ResultStorageDescriptor> getStorageDescriptors();
+ @Deprecated public final class TruncatedFilter {
+ ctor @Deprecated public TruncatedFilter(android.bluetooth.le.ScanFilter, java.util.List<android.bluetooth.le.ResultStorageDescriptor>);
+ method @Deprecated public android.bluetooth.le.ScanFilter getFilter();
+ method @Deprecated public java.util.List<android.bluetooth.le.ResultStorageDescriptor> getStorageDescriptors();
}
}
@@ -2848,7 +2859,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000
field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
- field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
+ field @Deprecated public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_KNOWN_SIGNER = 134217728; // 0x8000000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
@@ -3297,7 +3308,9 @@ package android.hardware.hdmi {
method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerStateChangeOnActiveSourceLost();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getRoutingControl();
method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioControl();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvSendStandbyOnSleep();
@@ -3313,7 +3326,9 @@ package android.hardware.hdmi {
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setRoutingControl(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioControl(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvSendStandbyOnSleep(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvWakeOnOneTouchPlay(@NonNull int);
@@ -3323,6 +3338,8 @@ package android.hardware.hdmi {
field public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode";
field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
+ field public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+ field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL = "system_audio_control";
field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
field public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
field public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = "tv_wake_on_one_touch_play";
@@ -3380,6 +3397,7 @@ package android.hardware.hdmi {
field public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
field public static final String POWER_CONTROL_MODE_NONE = "none";
field public static final String POWER_CONTROL_MODE_TV = "to_tv";
+ field public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
field public static final int POWER_STATUS_ON = 0; // 0x0
@@ -3395,6 +3413,10 @@ package android.hardware.hdmi {
field public static final int RESULT_SUCCESS = 0; // 0x0
field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
field public static final int RESULT_TIMEOUT = 1; // 0x1
+ field public static final int ROUTING_CONTROL_DISABLED = 0; // 0x0
+ field public static final int ROUTING_CONTROL_ENABLED = 1; // 0x1
+ field public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0; // 0x0
+ field public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1; // 0x1
field public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0; // 0x0
field public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1; // 0x1
field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
@@ -5388,6 +5410,8 @@ package android.media.audiopolicy {
field public static final int MIX_STATE_DISABLED = -1; // 0xffffffff
field public static final int MIX_STATE_IDLE = 0; // 0x0
field public static final int MIX_STATE_MIXING = 1; // 0x1
+ field public static final int MIX_TYPE_PLAYERS = 0; // 0x0
+ field public static final int MIX_TYPE_RECORDERS = 1; // 0x1
field public static final int ROUTE_FLAG_LOOP_BACK = 2; // 0x2
field public static final int ROUTE_FLAG_RENDER = 1; // 0x1
}
@@ -5401,6 +5425,7 @@ package android.media.audiopolicy {
}
public class AudioMixingRule {
+ method public int getTargetMixType();
field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
field public static final int RULE_MATCH_UID = 4; // 0x4
@@ -5415,6 +5440,7 @@ package android.media.audiopolicy {
method public android.media.audiopolicy.AudioMixingRule build();
method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException;
method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
+ method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder setTargetMixType(int);
}
public class AudioPolicy {
@@ -8059,8 +8085,12 @@ package android.os {
}
public final class BatteryStatsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanReset();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanResults(@NonNull android.os.WorkSource, int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStarted(@NonNull android.os.WorkSource, boolean);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStopped(@NonNull android.os.WorkSource, boolean);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
@@ -9053,6 +9083,7 @@ package android.provider {
public final class DeviceConfig {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
@@ -9105,6 +9136,8 @@ package android.provider {
field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+ field public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT = "surface_flinger_native_boot";
+ field public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
field public static final String NAMESPACE_SYSTEM_TIME = "system_time";
field public static final String NAMESPACE_TELEPHONY = "telephony";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index dd7c6db5d8fb..e00c16da817c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1281,6 +1281,12 @@ package android.hardware.soundtrigger {
package android.inputmethodservice {
+ public abstract class AbstractInputMethodService extends android.window.WindowProviderService implements android.view.KeyEvent.Callback {
+ method public final int getInitialDisplayId();
+ method @Nullable public final android.os.Bundle getWindowContextOptions();
+ method public final int getWindowType();
+ }
+
@UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L
}
@@ -1397,6 +1403,7 @@ package android.location {
package android.media {
public final class AudioAttributes implements android.os.Parcelable {
+ method public static int[] getSdkUsages();
method @NonNull public static String usageToXsdString(int);
method public static int xsdStringToUsage(@NonNull String);
}
@@ -1421,6 +1428,9 @@ package android.media {
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+ method public static final int[] getPublicStreamTypes();
+ method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
+ method public int getStreamMinVolumeInt(int);
method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
@@ -1445,6 +1455,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance();
method public static final int getNumStreamTypes();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float);
+ method @NonNull public static String streamToString(int);
field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
field public static final int DEVICE_ROLE_NONE = 0; // 0x0
field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
@@ -1542,6 +1553,13 @@ package android.media.audiopolicy {
method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
}
+ public final class AudioProductStrategy implements android.os.Parcelable {
+ method @NonNull public static android.media.AudioAttributes getDefaultAttributes();
+ method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public int getVolumeGroupIdForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public int getVolumeGroupIdForLegacyStreamType(int);
+ }
+
}
package android.media.metrics {
@@ -2120,6 +2138,7 @@ package android.provider {
public final class DeviceConfig {
field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
field public static final String NAMESPACE_ANDROID = "android";
+ field public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
@@ -2864,6 +2883,10 @@ package android.view.accessibility {
method public long getAccessibilityIdForRegion(@NonNull android.graphics.Region);
}
+ public class AccessibilityRecord {
+ method public void setDisplayId(int);
+ }
+
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
}
@@ -3256,6 +3279,7 @@ package android.window {
@UiContext public abstract class WindowProviderService extends android.app.Service {
ctor public WindowProviderService();
method public final void attachToWindowToken(@NonNull android.os.IBinder);
+ method @NonNull public int getInitialDisplayId();
method @Nullable public android.os.Bundle getWindowContextOptions();
method public abstract int getWindowType();
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 5409165542f4..e3690e55f4da 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -71,6 +71,10 @@ ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_LIST:
+AllUpper: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+ Constant field names must be named with only upper case characters: `android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes`, should be `S_DEFAULT_ATTRIBUTES`?
+
+
ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
@@ -537,6 +541,8 @@ InterfaceConstant: android.telecom.PhoneAccountSuggestionService#SERVICE_INTERFA
+InternalField: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+ Internal field sDefaultAttributes must not be exposed
InternalField: android.telephony.ims.ImsConferenceState#mParticipants:
@@ -1045,8 +1051,14 @@ MissingNullability: android.location.LocationManager#getTestProviderCurrentReque
MissingNullability: android.location.LocationRequest#writeToParcel(android.os.Parcel, int) parameter #0:
+MissingNullability: android.media.AudioAttributes#SDK_USAGES:
+ Missing nullability on field `SDK_USAGES` in class `class android.media.AudioAttributes`
+MissingNullability: android.media.AudioAttributes#getSdkUsages():
+ Missing nullability on method `getSdkUsages` return
MissingNullability: android.media.AudioFocusInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+MissingNullability: android.media.AudioManager#getPublicStreamTypes():
+ Missing nullability on method `getPublicStreamTypes` return
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
@@ -1063,6 +1075,8 @@ MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConf
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
+MissingNullability: android.media.AudioSystem#streamToString(int):
+ Missing nullability on method `streamToString` return
MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 6c001f305ce7..89d93ae307aa 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -313,6 +313,10 @@ aidl_interface {
backend: {
rust: {
enabled: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt", // for virtualizationservice
+ ],
},
},
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index e91209c1a273..f47995ca0efe 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
@@ -27,6 +29,7 @@ import android.annotation.TestApi;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
@@ -36,6 +39,7 @@ import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -102,14 +106,14 @@ import java.util.function.Consumer;
* An accessibility is declared as any other service in an AndroidManifest.xml, but it
* must do two things:
* <ul>
- * <ol>
+ * <li>
* Specify that it handles the "android.accessibilityservice.AccessibilityService"
* {@link android.content.Intent}.
- * </ol>
- * <ol>
+ * </li>
+ * <li>
* Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
* ensure that only the system can bind to it.
- * </ol>
+ * </li>
* </ul>
* If either of these items is missing, the system will ignore the accessibility service.
* Following is an example declaration:
@@ -961,30 +965,31 @@ public abstract class AccessibilityService extends Service {
}
}
+ @NonNull
@Override
public Context createDisplayContext(Display display) {
- final Context context = super.createDisplayContext(display);
- final int displayId = display.getDisplayId();
- setDefaultTokenInternal(context, displayId);
- return context;
+ return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
}
- private void setDefaultTokenInternal(Context context, int displayId) {
- final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE);
- final IAccessibilityServiceConnection connection =
- AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
- IBinder token = null;
- if (connection != null) {
- synchronized (mLock) {
- try {
- token = connection.getOverlayWindowToken(displayId);
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Failed to get window token", re);
- re.rethrowFromSystemServer();
- }
- }
- wm.setDefaultToken(token);
+ @NonNull
+ @Override
+ public Context createWindowContext(int type, @Nullable Bundle options) {
+ final Context context = super.createWindowContext(type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
}
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(@NonNull Display display, int type,
+ @Nullable Bundle options) {
+ final Context context = super.createWindowContext(display, type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
}
/**
@@ -2069,6 +2074,10 @@ public abstract class AccessibilityService extends Service {
if (WINDOW_SERVICE.equals(name)) {
if (mWindowManager == null) {
mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
+ final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
+ // Set e default token obtained from the connection to ensure client could use
+ // accessibility overlay.
+ wm.setDefaultToken(mWindowToken);
}
return mWindowManager;
}
@@ -2177,8 +2186,10 @@ public abstract class AccessibilityService extends Service {
// The client may have already obtained the window manager, so
// update the default token on whatever manager we gave them.
- final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
- wm.setDefaultToken(windowToken);
+ if (mWindowManager != null) {
+ final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
+ wm.setDefaultToken(mWindowToken);
+ }
}
@Override
@@ -2400,6 +2411,14 @@ public abstract class AccessibilityService extends Service {
if (connection != null) {
AccessibilityInteractionClient.getInstance(mContext).addConnection(
mConnectionId, connection);
+ if (mContext != null) {
+ try {
+ connection.setAttributionTag(mContext.getAttributionTag());
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting attributionTag", re);
+ re.rethrowFromSystemServer();
+ }
+ }
mCallback.init(mConnectionId, windowToken);
mCallback.onServiceConnected();
} else {
@@ -2675,4 +2694,58 @@ public abstract class AccessibilityService extends Service {
}
}
}
+
+ private static class AccessibilityContext extends ContextWrapper {
+ private final int mConnectionId;
+
+ private AccessibilityContext(Context base, int connectionId) {
+ super(base);
+ mConnectionId = connectionId;
+ setDefaultTokenInternal(this, getDisplayId());
+ }
+
+ @NonNull
+ @Override
+ public Context createDisplayContext(Display display) {
+ return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(int type, @Nullable Bundle options) {
+ final Context context = super.createWindowContext(type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(@NonNull Display display, int type,
+ @Nullable Bundle options) {
+ final Context context = super.createWindowContext(display, type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ private void setDefaultTokenInternal(Context context, int displayId) {
+ final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(
+ WINDOW_SERVICE);
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getConnection(mConnectionId);
+ IBinder token = null;
+ if (connection != null) {
+ try {
+ token = connection.getOverlayWindowToken(displayId);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to get window token", re);
+ re.rethrowFromSystemServer();
+ }
+ wm.setDefaultToken(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/accessibilityservice/AccessibilityTrace.java b/core/java/android/accessibilityservice/AccessibilityTrace.java
new file mode 100644
index 000000000000..f28015ab4685
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityTrace.java
@@ -0,0 +1,216 @@
+/**
+ * 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.accessibilityservice;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface to log accessibility trace.
+ *
+ * @hide
+ */
+public interface AccessibilityTrace {
+ String NAME_ACCESSIBILITY_SERVICE_CONNECTION = "IAccessibilityServiceConnection";
+ String NAME_ACCESSIBILITY_SERVICE_CLIENT = "IAccessibilityServiceClient";
+ String NAME_ACCESSIBILITY_MANAGER = "IAccessibilityManager";
+ String NAME_ACCESSIBILITY_MANAGER_CLIENT = "IAccessibilityManagerClient";
+ String NAME_ACCESSIBILITY_INTERACTION_CONNECTION = "IAccessibilityInteractionConnection";
+ String NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK =
+ "IAccessibilityInteractionConnectionCallback";
+ String NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = "IRemoteMagnificationAnimationCallback";
+ String NAME_WINDOW_MAGNIFICATION_CONNECTION = "IWindowMagnificationConnection";
+ String NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = "IWindowMagnificationConnectionCallback";
+ String NAME_WINDOW_MANAGER_INTERNAL = "WindowManagerInternal";
+ String NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = "WindowsForAccessibilityCallback";
+ String NAME_MAGNIFICATION_CALLBACK = "MagnificationCallbacks";
+ String NAME_INPUT_FILTER = "InputFilter";
+ String NAME_GESTURE = "Gesture";
+ String NAME_ACCESSIBILITY_SERVICE = "AccessibilityService";
+ String NAME_PACKAGE_BROADCAST_RECEIVER = "PMBroadcastReceiver";
+ String NAME_USER_BROADCAST_RECEIVER = "UserBroadcastReceiver";
+ String NAME_FINGERPRINT = "FingerprintGesture";
+ String NAME_ACCESSIBILITY_INTERACTION_CLIENT = "AccessibilityInteractionClient";
+
+ String NAME_ALL_LOGGINGS = "AllLoggings";
+ String NAME_NONE = "None";
+
+ long FLAGS_ACCESSIBILITY_SERVICE_CONNECTION = 0x0000000000000001L;
+ long FLAGS_ACCESSIBILITY_SERVICE_CLIENT = 0x0000000000000002L;
+ long FLAGS_ACCESSIBILITY_MANAGER = 0x0000000000000004L;
+ long FLAGS_ACCESSIBILITY_MANAGER_CLIENT = 0x0000000000000008L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION = 0x0000000000000010L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = 0x0000000000000020L;
+ long FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = 0x0000000000000040L;
+ long FLAGS_WINDOW_MAGNIFICATION_CONNECTION = 0x0000000000000080L;
+ long FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = 0x0000000000000100L;
+ long FLAGS_WINDOW_MANAGER_INTERNAL = 0x0000000000000200L;
+ long FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = 0x0000000000000400L;
+ long FLAGS_MAGNIFICATION_CALLBACK = 0x0000000000000800L;
+ long FLAGS_INPUT_FILTER = 0x0000000000001000L;
+ long FLAGS_GESTURE = 0x0000000000002000L;
+ long FLAGS_ACCESSIBILITY_SERVICE = 0x0000000000004000L;
+ long FLAGS_PACKAGE_BROADCAST_RECEIVER = 0x0000000000008000L;
+ long FLAGS_USER_BROADCAST_RECEIVER = 0x0000000000010000L;
+ long FLAGS_FINGERPRINT = 0x0000000000020000L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CLIENT = 0x0000000000040000L;
+
+ long FLAGS_LOGGING_NONE = 0x0000000000000000L;
+ long FLAGS_LOGGING_ALL = 0xFFFFFFFFFFFFFFFFL;
+
+ long FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES = FLAGS_ACCESSIBILITY_INTERACTION_CLIENT
+ | FLAGS_ACCESSIBILITY_SERVICE
+ | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION
+ | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
+ Map<String, Long> sNamesToFlags = Map.ofEntries(
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE_CONNECTION, FLAGS_ACCESSIBILITY_SERVICE_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE_CLIENT, FLAGS_ACCESSIBILITY_SERVICE_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_MANAGER, FLAGS_ACCESSIBILITY_MANAGER),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_MANAGER_CLIENT, FLAGS_ACCESSIBILITY_MANAGER_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CONNECTION,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK,
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MAGNIFICATION_CONNECTION, FLAGS_WINDOW_MAGNIFICATION_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MANAGER_INTERNAL, FLAGS_WINDOW_MANAGER_INTERNAL),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_MAGNIFICATION_CALLBACK, FLAGS_MAGNIFICATION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_INPUT_FILTER, FLAGS_INPUT_FILTER),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_GESTURE, FLAGS_GESTURE),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE, FLAGS_ACCESSIBILITY_SERVICE),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_PACKAGE_BROADCAST_RECEIVER, FLAGS_PACKAGE_BROADCAST_RECEIVER),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_USER_BROADCAST_RECEIVER, FLAGS_USER_BROADCAST_RECEIVER),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_FINGERPRINT, FLAGS_FINGERPRINT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CLIENT, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_NONE, FLAGS_LOGGING_NONE),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_ALL_LOGGINGS, FLAGS_LOGGING_ALL));
+
+ /**
+ * Get the flags of the logging types by the given names.
+ * The names list contains logging type names in lower case.
+ */
+ static long getLoggingFlagsFromNames(List<String> names) {
+ long types = FLAGS_LOGGING_NONE;
+ for (String name : names) {
+ long flag = sNamesToFlags.get(name);
+ types |= flag;
+ }
+ return types;
+ }
+
+ /**
+ * Get the list of the names of logging types by the given flags.
+ */
+ static List<String> getNamesOfLoggingTypes(long flags) {
+ List<String> list = new ArrayList<String>();
+
+ for (Map.Entry<String, Long> entry : sNamesToFlags.entrySet()) {
+ if ((entry.getValue() & flags) != FLAGS_LOGGING_NONE) {
+ list.add(entry.getKey());
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Whether the trace is enabled for any logging type.
+ */
+ boolean isA11yTracingEnabled();
+
+ /**
+ * Whether the trace is enabled for any of the given logging type.
+ */
+ boolean isA11yTracingEnabledForTypes(long typeIdFlags);
+
+ /**
+ * Get trace state to be sent to AccessibilityManager.
+ */
+ int getTraceStateForAccessibilityManagerClientState();
+
+ /**
+ * Start tracing for the given logging types.
+ */
+ void startTrace(long flagss);
+
+ /**
+ * Stop tracing.
+ */
+ void stopTrace();
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to search through the
+ * tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ */
+ void logTrace(String where, long loggingFlags);
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ * @param callingParams The parameters for the method to be logged.
+ */
+ void logTrace(String where, long loggingFlags, String callingParams);
+
+ /**
+ * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
+ * make screen content related requests use this API to log entry when receive callback.
+ * @param timestamp The timestamp when a callback is received.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ * @param callingParams The parameters for the callback.
+ * @param processId The process id of the calling component.
+ * @param threadId The threadId of the calling component.
+ * @param callingUid The calling uid of the callback.
+ * @param callStack The call stack of the callback.
+ * @param ignoreStackElements ignore these call stack element
+ */
+ void logTrace(long timestamp, String where, long loggingFlags, String callingParams,
+ int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+ Set<String> ignoreStackElements);
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 923b6f41414a..6c360e50a7bd 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -39,6 +39,8 @@ interface IAccessibilityServiceConnection {
void setServiceInfo(in AccessibilityServiceInfo info);
+ void setAttributionTag(in String attributionTag);
+
String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
@@ -118,6 +120,6 @@ interface IAccessibilityServiceConnection {
void setFocusAppearance(int strokeWidth, int color);
- oneway void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
+ oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
}
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 0d6a07938e95..e6cdcc0ee742 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -31,6 +31,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import java.util.Objects;
import java.util.Set;
/**
@@ -86,6 +87,12 @@ public class Account implements Parcelable {
if (TextUtils.isEmpty(type)) {
throw new IllegalArgumentException("the type must not be empty: " + type);
}
+ if (name.length() > 200) {
+ throw new IllegalArgumentException("account name is longer than 200 characters");
+ }
+ if (type.length() > 200) {
+ throw new IllegalArgumentException("account type is longer than 200 characters");
+ }
this.name = name;
this.type = type;
this.accessId = accessId;
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
index 8dcc04a27af6..6ad9d924f171 100644
--- a/core/java/android/accounts/OWNERS
+++ b/core/java/android/accounts/OWNERS
@@ -1,9 +1,4 @@
-carlosvaldivia@google.com
+jcivelli@google.com
dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
yamasani@google.com
omakoto@google.com
-
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4a7fcd232ce9..a83662592513 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -476,6 +476,19 @@ public class ActivityTaskManager {
}
/**
+ * Detaches the navigation bar from the app it was attached to during a transition.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+ try {
+ getService().detachNavigationBarFromApp(transition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Information you can retrieve about a root task in the system.
* @hide
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3915abe1e9eb..f6bcfd6f6dd3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -18,7 +18,6 @@ package android.app;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull;
-import static android.app.ConfigurationController.freeTextLayoutCachesIfNeeded;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
@@ -31,6 +30,10 @@ import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.view.Display.INVALID_DISPLAY;
+import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
+import static android.window.ConfigurationHelper.isDifferentDisplay;
+import static android.window.ConfigurationHelper.shouldUpdateResources;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -88,10 +91,8 @@ import android.database.sqlite.SQLiteDebug.DbStats;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
-import android.graphics.Rect;
import android.graphics.Typeface;
import android.hardware.display.DisplayManagerGlobal;
-import android.inputmethodservice.InputMethodService;
import android.media.MediaFrameworkInitializer;
import android.media.MediaFrameworkPlatformInitializer;
import android.media.MediaServiceManager;
@@ -183,6 +184,7 @@ import android.webkit.WebView;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
import android.window.SplashScreenView;
+import android.window.WindowProviderService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -5428,6 +5430,12 @@ public final class ActivityThread extends ClientTransactionHandler
// behave properly when activity is relaunching.
r.window.clearContentView();
} 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.
+ viewRoot.setActivityConfigCallback(null);
+ }
wm.removeViewImmediate(v);
}
}
@@ -5751,7 +5759,7 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
+ public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {
ArrayList<ComponentCallbacks2> callbacks
= new ArrayList<ComponentCallbacks2>();
@@ -5760,7 +5768,7 @@ public final class ActivityThread extends ClientTransactionHandler
for (int i=0; i<NAPP; i++) {
callbacks.add(mAllApplications.get(i));
}
- if (includeActivities) {
+ if (includeUiContexts) {
for (int i = mActivities.size() - 1; i >= 0; i--) {
final Activity a = mActivities.valueAt(i).activity;
if (a != null && !a.mFinished) {
@@ -5770,11 +5778,12 @@ public final class ActivityThread extends ClientTransactionHandler
}
final int NSVC = mServices.size();
for (int i=0; i<NSVC; i++) {
- final ComponentCallbacks2 serviceComp = mServices.valueAt(i);
- if (serviceComp instanceof InputMethodService) {
- mHasImeComponent = true;
+ final Service service = mServices.valueAt(i);
+ // If {@code includeUiContext} is set to false, WindowProviderService should not be
+ // collected because WindowProviderService is a UI Context.
+ if (includeUiContexts || !(service instanceof WindowProviderService)) {
+ callbacks.add(service);
}
- callbacks.add(serviceComp);
}
}
synchronized (mProviderMap) {
@@ -5829,35 +5838,25 @@ public final class ActivityThread extends ClientTransactionHandler
// change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
handleWindowingModeChangeIfNeeded(activity, newConfig);
- final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
- boolean shouldReportChange = false;
- if (activity.mCurrentConfig == null) {
- shouldReportChange = true;
- } else {
- // If the new config is the same as the config this Activity is already running with and
- // the override config also didn't change, then don't bother calling
- // onConfigurationChanged.
- // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
- // ResourcesImpl constructions.
- int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
- final ActivityClientRecord cr = getActivityClient(activityToken);
- diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig,
- cr != null ? cr.mSizeConfigurations : null);
-
- if (diff == 0) {
- if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
- && !movedToDifferentDisplay
- && mResourcesManager.isSameResourcesOverrideConfig(
- activityToken, amOverrideConfig)) {
- // Nothing significant, don't proceed with updating and reporting.
- return null;
- }
- } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
+ final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),
+ displayId);
+ final ActivityClientRecord r = mActivities.get(activityToken);
+ final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig,
+ newConfig, r != null ? r.mSizeConfigurations : null);
+ final boolean hasPublicConfigChange = diff != 0;
+ // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
+ // ResourcesImpl constructions.
+ final boolean shouldUpdateResources = hasPublicConfigChange
+ || shouldUpdateResources(activityToken, activity.mCurrentConfig, newConfig,
+ amOverrideConfig, movedToDifferentDisplay, hasPublicConfigChange);
+ final boolean shouldReportChange = hasPublicConfigChange
// If this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged. Otherwise, report to the activity for the
// changes.
- shouldReportChange = true;
- }
+ && (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0;
+ // Nothing significant, don't proceed with updating and reporting.
+ if (!shouldUpdateResources) {
+ return null;
}
// Propagate the configuration change to ResourcesManager and Activity.
@@ -5908,26 +5907,6 @@ public final class ActivityThread extends ClientTransactionHandler
return configToReport;
}
- // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
- // constructions.
- /**
- * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
- * should be updated.
- *
- * @see WindowManager#getCurrentWindowMetrics()
- * @see WindowManager#getMaximumWindowMetrics()
- */
- private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
- @NonNull Configuration newConfig) {
- final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
- final Rect newBounds = newConfig.windowConfiguration.getBounds();
-
- final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
- final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
-
- return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
- }
-
public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
mResourcesManager.applyConfigurationToResources(config, null);
@@ -6071,7 +6050,8 @@ public final class ActivityThread extends ClientTransactionHandler
// display.
displayId = r.activity.getDisplayId();
}
- final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
+ final boolean movedToDifferentDisplay = isDifferentDisplay(
+ r.activity.getDisplayId(), displayId);
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
&& !movedToDifferentDisplay) {
if (DEBUG_CONFIGURATION) {
@@ -6107,14 +6087,6 @@ public final class ActivityThread extends ClientTransactionHandler
mSomeActivitiesChanged = true;
}
- /**
- * Checks if the display id of activity is different from the given one. Note that
- * {@link Display#INVALID_DISPLAY} means no difference.
- */
- private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
- return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
- }
-
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
if (start) {
try {
@@ -6294,7 +6266,7 @@ public final class ActivityThread extends ClientTransactionHandler
final void handleLowMemory() {
final ArrayList<ComponentCallbacks2> callbacks =
- collectComponentCallbacks(true /* includeActivities */);
+ collectComponentCallbacks(true /* includeUiContexts */);
final int N = callbacks.size();
for (int i=0; i<N; i++) {
@@ -6327,7 +6299,7 @@ public final class ActivityThread extends ClientTransactionHandler
}
final ArrayList<ComponentCallbacks2> callbacks =
- collectComponentCallbacks(true /* includeActivities */);
+ collectComponentCallbacks(true /* includeUiContexts */);
final int N = callbacks.size();
for (int i = 0; i < N; i++) {
@@ -7546,12 +7518,6 @@ public final class ActivityThread extends ClientTransactionHandler
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
synchronized (mResourcesManager) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && mHasImeComponent) {
- Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, "
- + "config=" + globalConfig);
- }
-
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResources(globalConfig,
@@ -7906,11 +7872,6 @@ public final class ActivityThread extends ClientTransactionHandler
return mDensityCompatMode;
}
- @Override
- public boolean hasImeComponent() {
- return mHasImeComponent;
- }
-
// ------------------ Regular JNI ------------------------
private native void nPurgePendingResources();
private native void nDumpGraphicsInfo(FileDescriptor fd);
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index d91933c0f817..bc698f657305 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -32,11 +32,9 @@ interface ActivityThreadInternal {
boolean isInDensityCompatMode();
- boolean hasImeComponent();
-
boolean isCachedProcessState();
Application getApplication();
- ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities);
+ ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cd2c12cb4b6f..2d172c5b688d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -317,6 +317,16 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+ try {
+ return mPM.getLaunchIntentSenderForPackage(packageName, mContext.getPackageName(),
+ mContext.getAttributionTag(), getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public int[] getPackageGids(String packageName) throws NameNotFoundException {
return getPackageGids(packageName, 0);
}
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index f79e0780ecae..8637e31eb122 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -17,24 +17,20 @@
package android.app;
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentCallbacks2;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.LocaleList;
import android.os.Trace;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.WindowManagerGlobal;
@@ -169,12 +165,7 @@ class ConfigurationController {
mPendingConfiguration = null;
}
- final boolean hasIme = mActivityThread.hasImeComponent();
if (config == null) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && hasIme) {
- Log.w(TAG, "handleConfigurationChanged for IME app but config is null");
- }
return;
}
@@ -205,12 +196,6 @@ class ConfigurationController {
mConfiguration = new Configuration();
}
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && hasIme) {
- Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete "
- + ", config=" + config
- + ", mConfiguration=" + mConfiguration);
- }
return;
}
@@ -228,7 +213,7 @@ class ConfigurationController {
}
final ArrayList<ComponentCallbacks2> callbacks =
- mActivityThread.collectComponentCallbacks(false /* includeActivities */);
+ mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
freeTextLayoutCachesIfNeeded(configDiff);
@@ -238,13 +223,6 @@ class ConfigurationController {
ComponentCallbacks2 cb = callbacks.get(i);
if (!equivalent) {
performConfigurationChanged(cb, config);
- } else {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) {
- Log.w(TAG, "performConfigurationChanged didn't callback to IME "
- + ", configDiff=" + configDiff
- + ", mConfiguration=" + mConfiguration);
- }
}
}
}
@@ -326,16 +304,4 @@ class ConfigurationController {
return newConfig;
}
- /** Ask test layout engine to free its caches if there is a locale change. */
- static void freeTextLayoutCachesIfNeeded(int configDiff) {
- if (configDiff != 0) {
- boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
- if (hasLocaleConfigChange) {
- Canvas.freeTextLayoutCaches();
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Cleared TextLayout Caches");
- }
- }
- }
- }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f52fdc562b13..249a60633ff0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2721,10 +2721,13 @@ class ContextImpl extends Context {
// need to override their display in ResourcesManager.
baseContext.mForceDisplayOverrideInResources = false;
baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
- baseContext.mDisplay = display;
final Resources windowContextResources = createWindowContextResources(baseContext);
baseContext.setResources(windowContextResources);
+ // Associate the display with window context resources so that configuration update from
+ // the server side will also apply to the display's metrics.
+ baseContext.mDisplay = ResourcesManager.getInstance()
+ .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
return baseContext;
}
@@ -3187,12 +3190,6 @@ class ContextImpl extends Context {
@UnsupportedAppUsage
final void setOuterContext(@NonNull Context context) {
mOuterContext = context;
- // TODO(b/149463653): check if we still need this method after migrating IMS to
- // WindowContext.
- if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) {
- mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
- mIsConfigurationBasedContext = true;
- }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 5964f71d28db..b324fb68ff59 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -135,6 +135,7 @@ public final class GameManager {
throw e.rethrowFromSystemServer();
}
}
+
/**
* Returns a list of supported game modes for a given package.
* <p>
@@ -151,4 +152,19 @@ public final class GameManager {
}
}
+ /**
+ * Returns if ANGLE is enabled for a given package and user ID.
+ * <p>
+ * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @GameMode boolean getAngleEnabled(@NonNull String packageName) {
+ try {
+ return mService.getAngleEnabled(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 74d51a0bcf63..2be78033ddf7 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -330,4 +330,18 @@ interface IActivityTaskManager {
* When the Picture-in-picture state has changed.
*/
void onPictureInPictureStateChanged(in PictureInPictureUiState pipState);
+
+ /**
+ * Re-attach navbar to the display during a recents transition.
+ * TODO(188595497): Remove this once navbar attachment is in shell.
+ */
+ void detachNavigationBarFromApp(in IBinder transition);
+
+ /**
+ * Marks a process as a delegate for the currently playing remote transition animation. This
+ * must be called from a process that is already a remote transition player or delegate. Any
+ * marked delegates are cleaned-up automatically at the end of the transition.
+ * @param caller is the IApplicationThread representing the calling process.
+ */
+ void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
}
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 4bf8a3f77bca..189f0a2fca23 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -23,4 +23,5 @@ interface IGameManagerService {
int getGameMode(String packageName, int userId);
void setGameMode(String packageName, int gameMode, int userId);
int[] getAvailableGameModes(String packageName);
+ boolean getAngleEnabled(String packageName, int userId);
} \ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9d149cf340e2..ab88e6c8424f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5539,12 +5539,11 @@ public class Notification implements Parcelable
/**
* Determines if the notification should be colorized *for the purposes of applying colors*.
- * If this is the minimized view of a colorized notification, or if the app did not provide
- * a color to colorize with, this will return false so that internal coloring logic can
- * still render the notification normally.
+ * If this is the minimized view of a colorized notification, this will return false so that
+ * internal coloring logic can still render the notification normally.
*/
private boolean isBackgroundColorized(StandardTemplateParams p) {
- return p.allowColorization && mN.color != COLOR_DEFAULT && mN.isColorized();
+ return p.allowColorization && mN.isColorized();
}
private boolean isCallActionColorCustomizable() {
@@ -5552,8 +5551,7 @@ public class Notification implements Parcelable
// that is only used for disallowing colorization of headers for the minimized state,
// and neither of those conditions applies when showing actions.
// Not requiring StandardTemplateParams as an argument simplifies the creation process.
- return mN.color != COLOR_DEFAULT && mN.isColorized()
- && mContext.getResources().getBoolean(
+ return mN.isColorized() && mContext.getResources().getBoolean(
R.bool.config_callNotificationActionColorsRequireColorized);
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6ca7dfbe2284..ef4d7b1f42e2 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -22,6 +22,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -66,12 +67,12 @@ import java.util.concurrent.atomic.AtomicLong;
*
* <pre>
* public class UserBirthdayServiceImpl implements IUserBirthdayService {
- * private final HashMap<Integer, Birthday> mUidToBirthday;
- * @Override
+ * private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
+ * {@literal @}Override
* public synchronized Birthday getUserBirthday(int userId) {
* return mUidToBirthday.get(userId);
* }
- * private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
* mUidToBirthday.clear();
* mUidToBirthday.putAll(uidToBirthday);
* }
@@ -105,9 +106,9 @@ import java.util.concurrent.atomic.AtomicLong;
* ...
* private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
* private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
- * private final PropertyInvalidatedCache<Integer, Birthday> mBirthdayCache = new
- * PropertyInvalidatedCache<Integer, Birthday>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- * @Override
+ * private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ * {@literal @}Override
* protected Birthday recompute(Integer userId) {
* return GetService("birthdayd").getUserBirthday(userId);
* }
@@ -140,7 +141,7 @@ import java.util.concurrent.atomic.AtomicLong;
* ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
* }
*
- * private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
* mUidToBirthday.clear();
* mUidToBirthday.putAll(uidToBirthday);
* ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
@@ -169,6 +170,41 @@ import java.util.concurrent.atomic.AtomicLong;
* this local case, there's no IPC, so use of the cache is (depending on exact
* circumstance) unnecessary.
*
+ * There may be queries for which it is more efficient to bypass the cache than to cache
+ * the result. This would be true, for example, if some queries would require frequent
+ * cache invalidation while other queries require infrequent invalidation. To expand on
+ * the birthday example, suppose that there is a userId that signifies "the next
+ * birthday". When passed this userId, the server returns the next birthday among all
+ * users - this value changes as time advances. The userId value can be cached, but the
+ * cache must be invalidated whenever a birthday occurs, and this invalidates all
+ * birthdays. If there is a large number of users, invalidation will happen so often that
+ * the cache provides no value.
+ *
+ * The class provides a bypass mechanism to handle this situation.
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
+ * private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
+ * private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ * {@literal @}Override
+ * protected Birthday recompute(Integer userId) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * {@literal @}Override
+ * protected boolean bypass(Integer userId) {
+ * return userId == NEXT_BIRTHDAY;
+ * }
+ * };
+ * ...
+ * }
+ * </pre>
+ *
+ * If the {@code bypass()} method returns true then the cache is not used for that
+ * particular query. The {@code bypass()} method is not abstract and the default
+ * implementation returns false.
+ *
* For security, there is a allowlist of processes that are allowed to invalidate a cache.
* The allowlist includes normal runtime processes but does not include test processes.
* Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable
@@ -190,19 +226,23 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public abstract class PropertyInvalidatedCache<Query, Result> {
/**
- * Reserved nonce values. The code is written assuming that these
- * values are contiguous.
+ * Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note
+ * that all values cause the cache to be skipped.
*/
private static final int NONCE_UNSET = 0;
private static final int NONCE_DISABLED = 1;
private static final int NONCE_CORKED = 2;
- private static final int NONCE_RESERVED = NONCE_CORKED + 1;
+ private static final int NONCE_BYPASS = 3;
+
+ private static boolean isReservedNonce(long n) {
+ return n >= NONCE_UNSET && n <= NONCE_BYPASS;
+ }
/**
* The names of the nonces
*/
private static final String[] sNonceName =
- new String[]{ "unset", "disabled", "corked" };
+ new String[]{ "unset", "disabled", "corked", "bypass" };
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
@@ -220,7 +260,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private long mMisses = 0;
@GuardedBy("mLock")
- private long mSkips[] = new long[]{ 0, 0, 0 };
+ private long[] mSkips = new long[]{ 0, 0, 0, 0 };
@GuardedBy("mLock")
private long mMissOverflow = 0;
@@ -363,6 +403,91 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * SystemProperties are protected and cannot be written (or read, usually) by random
+ * processes. So, for testing purposes, the methods have a bypass mode that reads and
+ * writes to a HashMap and does not go out to the SystemProperties at all.
+ */
+
+ // If true, the cache might be under test. If false, there is no testing in progress.
+ private static volatile boolean sTesting = false;
+
+ // If sTesting is true then keys that are under test are in this map.
+ private static final HashMap<String, Long> sTestingPropertyMap = new HashMap<>();
+
+ /**
+ * Enable or disable testing. The testing property map is cleared every time this
+ * method is called.
+ */
+ @VisibleForTesting
+ public static void setTestMode(boolean mode) {
+ sTesting = mode;
+ synchronized (sTestingPropertyMap) {
+ sTestingPropertyMap.clear();
+ }
+ }
+
+ /**
+ * Enable testing the specific cache key. Only keys in the map are subject to testing.
+ * There is no method to stop testing a property name. Just disable the test mode.
+ */
+ @VisibleForTesting
+ public static void testPropertyName(String name) {
+ synchronized (sTestingPropertyMap) {
+ sTestingPropertyMap.put(name, (long) NONCE_UNSET);
+ }
+ }
+
+ // Read the system property associated with the current cache. This method uses the
+ // handle for faster reading.
+ private long getCurrentNonce() {
+ if (sTesting) {
+ synchronized (sTestingPropertyMap) {
+ Long n = sTestingPropertyMap.get(mPropertyName);
+ if (n != null) {
+ return n;
+ }
+ }
+ }
+
+ SystemProperties.Handle handle = mPropertyHandle;
+ if (handle == null) {
+ handle = SystemProperties.find(mPropertyName);
+ if (handle == null) {
+ return NONCE_UNSET;
+ }
+ mPropertyHandle = handle;
+ }
+ return handle.getLong(NONCE_UNSET);
+ }
+
+ // Write the nonce in a static context. No handle is available.
+ private static void setNonce(String name, long val) {
+ if (sTesting) {
+ synchronized (sTestingPropertyMap) {
+ Long n = sTestingPropertyMap.get(name);
+ if (n != null) {
+ sTestingPropertyMap.put(name, val);
+ return;
+ }
+ }
+ }
+ SystemProperties.set(name, Long.toString(val));
+ }
+
+ // Set the nonce in a static context. No handle is available.
+ private static long getNonce(String name) {
+ if (sTesting) {
+ synchronized (sTestingPropertyMap) {
+ Long n = sTestingPropertyMap.get(name);
+ if (n != null) {
+ return n;
+ }
+ }
+ }
+ return SystemProperties.getLong(name, NONCE_UNSET);
+ }
+
+ /**
* Forget all cached values.
*/
public final void clear() {
@@ -418,18 +543,6 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
return oldResult;
}
- private long getCurrentNonce() {
- SystemProperties.Handle handle = mPropertyHandle;
- if (handle == null) {
- handle = SystemProperties.find(mPropertyName);
- if (handle == null) {
- return NONCE_UNSET;
- }
- mPropertyHandle = handle;
- }
- return handle.getLong(NONCE_UNSET);
- }
-
/**
* Disable the use of this cache in this process.
*/
@@ -477,9 +590,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public Result query(Query query) {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
+ if (bypass(query)) {
+ currentNonce = NONCE_BYPASS;
+ }
for (;;) {
- if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET
- || currentNonce == NONCE_CORKED || bypass(query)) {
+ if (isReservedNonce(currentNonce)) {
if (!mDisabled) {
// Do not bother collecting statistics if the cache is
// locally disabled.
@@ -490,7 +605,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (DEBUG) {
if (!mDisabled) {
- Log.d(TAG, String.format(
+ Log.d(TAG, TextUtils.formatSimple(
"cache %s %s for %s",
cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
}
@@ -505,7 +620,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (cachedResult != null) mHits++;
} else {
if (DEBUG) {
- Log.d(TAG, String.format(
+ Log.d(TAG, TextUtils.formatSimple(
"clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
cacheName(), mCache.size(),
mLastSeenNonce, currentNonce));
@@ -531,9 +646,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (currentNonce != afterRefreshNonce) {
currentNonce = afterRefreshNonce;
if (DEBUG) {
- Log.d(TAG, String.format("restarting %s %s because nonce changed in refresh",
- cacheName(),
- queryToString(query)));
+ Log.d(TAG, TextUtils.formatSimple(
+ "restarting %s %s because nonce changed in refresh",
+ cacheName(),
+ queryToString(query)));
}
continue;
}
@@ -602,7 +718,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (!sEnabled) {
return;
}
- SystemProperties.set(name, Long.toString(NONCE_DISABLED));
+ setNonce(name, NONCE_DISABLED);
}
/**
@@ -622,7 +738,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void invalidateCache(@NonNull String name) {
if (!sEnabled) {
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"cache invalidate %s suppressed", name));
}
return;
@@ -651,7 +767,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private static void invalidateCacheLocked(@NonNull String name) {
// There's no race here: we don't require that values strictly increase, but instead
// only that each is unique in a single runtime-restart session.
- final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+ final long nonce = getNonce(name);
if (nonce == NONCE_DISABLED) {
if (DEBUG) {
Log.d(TAG, "refusing to invalidate disabled cache: " + name);
@@ -662,18 +778,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
long newValue;
do {
newValue = NoPreloadHolder.next();
- } while (newValue >= 0 && newValue < NONCE_RESERVED);
- final String newValueString = Long.toString(newValue);
+ } while (isReservedNonce(newValue));
if (DEBUG) {
- Log.d(TAG,
- String.format("invalidating cache [%s]: [%s] -> [%s]",
- name,
- nonce,
- newValueString));
+ Log.d(TAG, TextUtils.formatSimple(
+ "invalidating cache [%s]: [%s] -> [%s]",
+ name, nonce, Long.toString(newValue)));
}
// TODO(dancol): add an atomic compare and exchange property set operation to avoid a
// small race with concurrent disable here.
- SystemProperties.set(name, newValueString);
+ setNonce(name, newValue);
long invalidateCount = sInvalidates.getOrDefault(name, (long) 0);
sInvalidates.put(name, ++invalidateCount);
}
@@ -693,7 +806,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void corkInvalidations(@NonNull String name) {
if (!sEnabled) {
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"cache cork %s suppressed", name));
}
return;
@@ -702,7 +815,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
- Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks));
+ Log.d(TAG, TextUtils.formatSimple(
+ "corking %s: numberCorks=%s", name, numberCorks));
}
// If we're the first ones to cork this cache, set the cache to the corked state so
@@ -714,9 +828,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
// uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
// Implement this more dangerous mode of operation if necessary.
if (numberCorks == 0) {
- final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+ final long nonce = getNonce(name);
if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
- SystemProperties.set(name, Long.toString(NONCE_CORKED));
+ setNonce(name, NONCE_CORKED);
}
} else {
final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
@@ -739,8 +853,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void uncorkInvalidations(@NonNull String name) {
if (!sEnabled) {
if (DEBUG) {
- Log.w(TAG, String.format(
- "cache uncork %s suppressed", name));
+ Log.w(TAG, TextUtils.formatSimple(
+ "cache uncork %s suppressed", name));
}
return;
}
@@ -748,7 +862,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
- Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks));
+ Log.d(TAG, TextUtils.formatSimple(
+ "uncorking %s: numberCorks=%s", name, numberCorks));
}
if (numberCorks < 1) {
@@ -816,7 +931,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
synchronized (mLock) {
boolean alreadyQueued = mUncorkDeadlineMs >= 0;
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
mUncorkDeadlineMs));
}
@@ -834,7 +949,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private void handleMessage(Message msg) {
synchronized (mLock) {
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"handleMsesage %s mUncorkDeadlineMs=%s",
mPropertyName, mUncorkDeadlineMs));
}
@@ -846,7 +961,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (mUncorkDeadlineMs > nowMs) {
mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"scheduling uncork at %s",
mUncorkDeadlineMs));
}
@@ -880,10 +995,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
Result resultToCompare = recompute(query);
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
- Log.e(TAG, String.format(
- "cache %s inconsistent for %s is %s should be %s",
- cacheName(), queryToString(query),
- proposedResult, resultToCompare));
+ Log.e(TAG, TextUtils.formatSimple(
+ "cache %s inconsistent for %s is %s should be %s",
+ cacheName(), queryToString(query),
+ proposedResult, resultToCompare));
}
// Always return the "true" result in verification mode.
return resultToCompare;
@@ -953,21 +1068,24 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
synchronized (mLock) {
- pw.println(String.format(" Cache Name: %s", cacheName()));
- pw.println(String.format(" Property: %s", mPropertyName));
- final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED];
- pw.println(String.format(" Hits: %d, Misses: %d, Skips: %d, Clears: %d",
+ pw.println(TextUtils.formatSimple(" Cache Name: %s", cacheName()));
+ pw.println(TextUtils.formatSimple(" Property: %s", mPropertyName));
+ final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
+ + mSkips[NONCE_BYPASS];
+ pw.println(TextUtils.formatSimple(
+ " Hits: %d, Misses: %d, Skips: %d, Clears: %d",
mHits, mMisses, skips, mClears));
- pw.println(String.format(" Skip-corked: %d, Skip-unset: %d, Skip-other: %d",
+ pw.println(TextUtils.formatSimple(
+ " Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d",
mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
- mSkips[NONCE_DISABLED]));
- pw.println(String.format(
+ mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED]));
+ pw.println(TextUtils.formatSimple(
" Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
mLastSeenNonce, invalidateCount, corkedInvalidates));
- pw.println(String.format(
+ pw.println(TextUtils.formatSimple(
" Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
- pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true"));
+ pw.println(TextUtils.formatSimple(" Enabled: %s", mDisabled ? "false" : "true"));
pw.println("");
Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
@@ -980,7 +1098,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
String key = Objects.toString(entry.getKey());
String value = Objects.toString(entry.getValue());
- pw.println(String.format(" Key: %s\n Value: %s\n", key, value));
+ pw.println(TextUtils.formatSimple(" Key: %s\n Value: %s\n", key, value));
}
}
}
@@ -1009,7 +1127,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
pw.println(" Corking Status:");
for (int i = 0; i < activeCorks.size(); i++) {
Map.Entry<String, Integer> entry = activeCorks.get(i);
- pw.println(String.format(" Property Name: %s Count: %d",
+ pw.println(TextUtils.formatSimple(" Property Name: %s Count: %d",
entry.getKey(), entry.getValue()));
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 198c33e83707..bf3778dfeecc 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -693,7 +693,7 @@ public class ResourcesManager {
* @return true if activity resources override config matches the provided one or they are both
* null, false otherwise.
*/
- boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
+ public boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
@Nullable Configuration overrideConfig) {
synchronized (mLock) {
final ActivityResources activityResources
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 85758a92fa98..cac763988d5e 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -17,6 +17,7 @@
package android.app;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -119,6 +120,12 @@ public class TaskInfo {
public int displayId;
/**
+ * The feature id of {@link com.android.server.wm.TaskDisplayArea} this task is associated with.
+ * @hide
+ */
+ public int displayAreaFeatureId = FEATURE_UNDEFINED;
+
+ /**
* The recent activity values for the highest activity in the stack to have set the values.
* {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
*/
@@ -243,6 +250,12 @@ public class TaskInfo {
*/
public boolean isVisible;
+ /**
+ * Whether this task is sleeping due to sleeping display.
+ * @hide
+ */
+ public boolean isSleeping;
+
TaskInfo() {
// Do nothing
}
@@ -329,11 +342,10 @@ public class TaskInfo {
}
/**
- * Returns {@code true} if parameters that are important for task organizers have changed
- * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners
- * about that.
- * @hide
- */
+ * Returns {@code true} if the parameters that are important for task organizers are equal
+ * between this {@link TaskInfo} and {@param that}.
+ * @hide
+ */
public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) {
if (that == null) {
return false;
@@ -341,13 +353,15 @@ public class TaskInfo {
return topActivityType == that.topActivityType
&& isResizeable == that.isResizeable
&& supportsMultiWindow == that.supportsMultiWindow
+ && displayAreaFeatureId == that.displayAreaFeatureId
&& Objects.equals(positionInParent, that.positionInParent)
&& Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
&& Objects.equals(displayCutoutInsets, that.displayCutoutInsets)
&& getWindowingMode() == that.getWindowingMode()
&& Objects.equals(taskDescription, that.taskDescription)
&& isFocused == that.isFocused
- && isVisible == that.isVisible;
+ && isVisible == that.isVisible
+ && isSleeping == that.isSleeping;
}
/**
@@ -402,8 +416,10 @@ public class TaskInfo {
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ isSleeping = source.readBoolean();
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
+ displayAreaFeatureId = source.readInt();
}
/**
@@ -440,8 +456,10 @@ public class TaskInfo {
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeBoolean(isSleeping);
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
+ dest.writeInt(displayAreaFeatureId);
}
@Override
@@ -468,8 +486,10 @@ public class TaskInfo {
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " isSleeping=" + isSleeping
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
- + " locusId= " + mTopActivityLocusId
+ + " locusId=" + mTopActivityLocusId
+ + " displayAreaFeatureId=" + displayAreaFeatureId
+ "}";
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6bc331d323ac..7e5b5a677efe 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5690,8 +5690,10 @@ public class DevicePolicyManager {
/**
* Disable text entry into notifications on secure keyguard screens (e.g. PIN/Pattern/Password).
- * This flag has no effect starting from version {@link android.os.Build.VERSION_CODES#N}
+ * @deprecated This flag was added in version {@link android.os.Build.VERSION_CODES#N}, but it
+ * never had any effect.
*/
+ @Deprecated
public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 1 << 6;
/**
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index fad6cd311021..ebc2945fb1a0 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -24,6 +24,7 @@ import android.os.Parcel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* An app compat override applied to a given package and change id pairing.
@@ -139,6 +140,22 @@ public final class PackageOverride {
/** @hide */
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PackageOverride that = (PackageOverride) o;
+ return mMinVersionCode == that.mMinVersionCode && mMaxVersionCode == that.mMaxVersionCode
+ && mEnabled == that.mEnabled;
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMinVersionCode, mMaxVersionCode, mEnabled);
+ }
+
+ /** @hide */
+ @Override
public String toString() {
if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
return Boolean.toString(mEnabled);
diff --git a/core/java/android/app/usage/OWNERS b/core/java/android/app/usage/OWNERS
index a33d0adaa9b6..9668f80e562e 100644
--- a/core/java/android/app/usage/OWNERS
+++ b/core/java/android/app/usage/OWNERS
@@ -3,3 +3,5 @@
yamasani@google.com
mwachens@google.com
varunshah@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f5ab2ab7448a..27241663d2a1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3059,6 +3059,9 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.LE_AUDIO) {
BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.VOLUME_CONTROL) {
+ BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
+ return true;
} else {
return false;
}
@@ -3151,6 +3154,11 @@ public final class BluetoothAdapter {
case BluetoothProfile.LE_AUDIO:
BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
leAudio.close();
+ break;
+ case BluetoothProfile.VOLUME_CONTROL:
+ BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
+ vcs.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index aea82102ca36..17a3e48e5d91 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -82,6 +82,9 @@ public final class BluetoothGatt implements BluetoothProfile {
private static final int CONN_STATE_DISCONNECTING = 3;
private static final int CONN_STATE_CLOSED = 4;
+ private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
+ private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds
+
private List<BluetoothGattService> mServices;
/** A GATT operation completed successfully */
@@ -134,6 +137,27 @@ public final class BluetoothGatt implements BluetoothProfile {
public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
/**
+ * A GATT writeCharacteristic request is started successfully.
+ *
+ * @hide
+ */
+ public static final int GATT_WRITE_REQUEST_SUCCESS = 0;
+
+ /**
+ * A GATT writeCharacteristic request failed to start.
+ *
+ * @hide
+ */
+ public static final int GATT_WRITE_REQUEST_FAIL = 1;
+
+ /**
+ * A GATT writeCharacteristic request is issued to a busy remote device.
+ *
+ * @hide
+ */
+ public static final int GATT_WRITE_REQUEST_BUSY = 2;
+
+ /**
* No authentication required.
*
* @hide
@@ -440,9 +464,19 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
- mService.writeCharacteristic(mClientIf, address, handle,
- characteristic.getWriteType(), authReq,
- characteristic.getValue(), mAttributionSource);
+ int requestStatus = GATT_WRITE_REQUEST_FAIL;
+ for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+ requestStatus = mService.writeCharacteristic(mClientIf, address, handle,
+ characteristic.getWriteType(), authReq,
+ characteristic.getValue(), mAttributionSource);
+ if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ break;
+ }
+ try {
+ Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
+ } catch (InterruptedException e) {
+ }
+ }
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -1190,7 +1224,9 @@ public final class BluetoothGatt implements BluetoothProfile {
characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1226,7 +1262,9 @@ public final class BluetoothGatt implements BluetoothProfile {
mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1264,21 +1302,35 @@ public final class BluetoothGatt implements BluetoothProfile {
if (device == null) return false;
synchronized (mDeviceBusyLock) {
- if (mDeviceBusy) return false;
+ if (mDeviceBusy) {
+ return false;
+ }
mDeviceBusy = true;
}
+ int requestStatus = GATT_WRITE_REQUEST_FAIL;
try {
- mService.writeCharacteristic(mClientIf, device.getAddress(),
+ for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+ requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
characteristic.getInstanceId(), characteristic.getWriteType(),
AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
+ if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ break;
+ }
+ try {
+ Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
+ } catch (InterruptedException e) {
+ }
+ }
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
- return true;
+ return requestStatus == GATT_WRITE_REQUEST_SUCCESS;
}
/**
@@ -1317,7 +1369,9 @@ public final class BluetoothGatt implements BluetoothProfile {
descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1359,7 +1413,9 @@ public final class BluetoothGatt implements BluetoothProfile {
AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1428,7 +1484,9 @@ public final class BluetoothGatt implements BluetoothProfile {
mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index 9de27ff97fe5..c438dd34f813 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -356,7 +356,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* earbud)
* @param device LE Audio capable device
* @return group id that this device currently belongs to
- * @hide
*/
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 161c843f0398..83a272fce311 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -208,17 +208,24 @@ public interface BluetoothProfile {
/**
* LE Audio Device
*
- * @hide
*/
int LE_AUDIO = 22;
/**
+ * Volume Control profile
+ *
+ * @hide
+ */
+ @SystemApi
+ int VOLUME_CONTROL = 23;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 22;
+ int MAX_PROFILE_ID = 23;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
new file mode 100644
index 000000000000..678c11a59f31
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.Manifest;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+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 java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Volume Control service.
+ *
+ * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothVolumeControl proxy object.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable {
+ private static final String TAG = "BluetoothVolumeControl";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private CloseGuard mCloseGuard;
+
+ /**
+ * Intent used to broadcast the change in connection state of the Volume Control
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+
+ private BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
+ private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
+ IBluetoothVolumeControl.class.getName()) {
+ @Override
+ public IBluetoothVolumeControl getServiceInterface(IBinder service) {
+ return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+ }
+ };
+
+ /**
+ * Create a BluetoothVolumeControl proxy object for interacting with the local
+ * Bluetooth Volume Control service.
+ */
+ /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
+ mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
+ }
+
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void close() {
+ mProfileConnector.disconnect();
+ }
+
+ private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); }
+
+ /**
+ * Get the list of connected devices. Currently at most one.
+ *
+ * @return list of connected devices
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) log("getConnectedDevices()");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * Get the list of devices matching specified states. Currently at most one.
+ *
+ * @return list of matching devices
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) log("getDevicesMatchingStates()");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * Get connection state of device
+ *
+ * @return device connection state
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) log("getConnectionState(" + device + ")");
+ final IBluetoothVolumeControl 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;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote device.
+ * Minimum value is 0 and maximum value is 255
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void setVolume(@Nullable BluetoothDevice device,
+ @IntRange(from = 0, to = 255) int 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");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Set connection policy of the profile
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @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;
+ }
+ try {
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the connection policy of the profile.
+ *
+ * <p> The connection policy can be any of:
+ * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Bluetooth device
+ * @return connection policy of the device
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
+ if (VDBG) log("getConnectionPolicy(" + device + ")");
+ final IBluetoothVolumeControl 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;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+
+ private boolean isEnabled() {
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
+ }
+
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 4e5ede74ce46..bb0b95649b17 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -937,17 +937,18 @@ public final class OobData implements Parcelable {
}
@NonNull
- private String toHexString(@NonNull int b) {
+ private String toHexString(int b) {
return toHexString(new byte[] {(byte) b});
}
@NonNull
- private String toHexString(@NonNull byte b) {
+ private String toHexString(byte b) {
return toHexString(new byte[] {b});
}
@NonNull
- private String toHexString(@NonNull byte[] array) {
+ private String toHexString(byte[] array) {
+ if (array == null) return "null";
StringBuilder builder = new StringBuilder(array.length * 2);
for (byte b: array) {
builder.append(String.format("%02x", b));
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 34aac8bfdb25..ee173dbc4ad4 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -151,7 +151,7 @@ public final class BluetoothLeScanner {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startScan(List<ScanFilter> filters, ScanSettings settings,
final ScanCallback callback) {
- startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
+ startScan(filters, settings, null, callback, /*callbackIntent=*/ null);
}
/**
@@ -185,7 +185,7 @@ public final class BluetoothLeScanner {
@NonNull PendingIntent callbackIntent) {
return startScan(filters,
settings != null ? settings : new ScanSettings.Builder().build(),
- null, null, callbackIntent, null);
+ null, null, callbackIntent);
}
/**
@@ -231,14 +231,13 @@ public final class BluetoothLeScanner {
@SuppressLint("AndroidFrameworkRequiresPermission")
public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback) {
- startScan(filters, settings, workSource, callback, null, null);
+ startScan(filters, settings, workSource, callback, null);
}
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
private int startScan(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback,
- final PendingIntent callbackIntent,
- List<List<ResultStorageDescriptor>> resultStorages) {
+ final PendingIntent callbackIntent) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null && callbackIntent == null) {
throw new IllegalArgumentException("callback is null");
@@ -274,7 +273,7 @@ public final class BluetoothLeScanner {
}
if (callback != null) {
BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
- settings, workSource, callback, resultStorages);
+ settings, workSource, callback);
wrapper.startRegistration();
} else {
try {
@@ -357,8 +356,11 @@ public final class BluetoothLeScanner {
/**
* Start truncated scan.
*
+ * @deprecated this is not used anywhere
+ *
* @hide
*/
+ @Deprecated
@SystemApi
@RequiresBluetoothScanPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@@ -366,13 +368,10 @@ public final class BluetoothLeScanner {
final ScanCallback callback) {
int filterSize = truncatedFilters.size();
List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
- List<List<ResultStorageDescriptor>> scanStorages =
- new ArrayList<List<ResultStorageDescriptor>>(filterSize);
for (TruncatedFilter filter : truncatedFilters) {
scanFilters.add(filter.getFilter());
- scanStorages.add(filter.getStorageDescriptors());
}
- startScan(scanFilters, settings, null, callback, null, scanStorages);
+ startScan(scanFilters, settings, null, callback, null);
}
/**
@@ -397,7 +396,6 @@ public final class BluetoothLeScanner {
private final WorkSource mWorkSource;
private ScanSettings mSettings;
private IBluetoothGatt mBluetoothGatt;
- private List<List<ResultStorageDescriptor>> mResultStorages;
// mLeHandle 0: not registered
// -2: registration failed because app is scanning to frequently
@@ -407,15 +405,13 @@ public final class BluetoothLeScanner {
public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
List<ScanFilter> filters, ScanSettings settings,
- WorkSource workSource, ScanCallback scanCallback,
- List<List<ResultStorageDescriptor>> resultStorages) {
+ WorkSource workSource, ScanCallback scanCallback) {
mBluetoothGatt = bluetoothGatt;
mFilters = filters;
mSettings = settings;
mWorkSource = workSource;
mScanCallback = scanCallback;
mScannerId = 0;
- mResultStorages = resultStorages;
}
public void startRegistration() {
@@ -493,7 +489,7 @@ public final class BluetoothLeScanner {
} else {
mScannerId = scannerId;
mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
- mResultStorages, mAttributionSource);
+ mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "fail to start le scan: " + e);
diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
index 796c815d69bb..f65048975deb 100644
--- a/core/java/android/bluetooth/le/ResultStorageDescriptor.java
+++ b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
@@ -23,8 +23,11 @@ import android.os.Parcelable;
/**
* Describes the way to store scan result.
*
+ * @deprecated this is not used anywhere
+ *
* @hide
*/
+@Deprecated
@SystemApi
public final class ResultStorageDescriptor implements Parcelable {
private int mType;
diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java
index 93f526bb9f09..25925888a0d2 100644
--- a/core/java/android/bluetooth/le/TruncatedFilter.java
+++ b/core/java/android/bluetooth/le/TruncatedFilter.java
@@ -24,8 +24,11 @@ import java.util.List;
/**
* A special scan filter that lets the client decide how the scan record should be stored.
*
+ * @deprecated this is not used anywhere
+ *
* @hide
*/
+@Deprecated
@SystemApi
@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class TruncatedFilter {
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 9007d9d8bbcc..b060ce2f4460 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -53,8 +53,6 @@ public final class Association implements Parcelable {
-
-
// Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
@@ -242,7 +240,7 @@ public final class Association implements Parcelable {
};
@DataClass.Generated(
- time = 1612832377589L,
+ time = 1611795283642L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\nprivate final long mTimeApprovedMs\npublic int getUserId()\nprivate java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 265ff331ffa0..ffd0d0bec328 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3289,7 +3289,7 @@ public abstract class ContentResolver implements ContentInterface {
* @param authority the provider to specify in the sync request
* @param extras extra parameters to go along with the sync request
* @param pollFrequency how frequently the sync should be performed, in seconds.
- * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+ * On Android API level 24 and above, a minimum interval of 15 minutes is enforced.
* On previous versions, the minimum interval is 1 hour.
* @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
* are null.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e35a32638a8..e109ae558ea2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1805,7 +1805,7 @@ public class Intent implements Parcelable, Cloneable {
* the package name of the current installed package to be uninstalled.
* You can optionally supply {@link #EXTRA_RETURN_RESULT}.
* <p>
- * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
+ * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the uninstall
* succeeded.
* <p>
* Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
@@ -5033,7 +5033,7 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
/**
- * An activity to run when device is inserted into a car dock.
+ * An activity to run when device is inserted into a desk dock.
* Used with {@link #ACTION_MAIN} to launch an activity. For more
* information, see {@link android.app.UiModeManager}.
*/
@@ -5253,6 +5253,30 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the weather application.
+ * The activity should be able to give the user information about the weather
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
+
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the fitness application.
+ * The activity should be able to give the user fitness information and manage workouts
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard extra data keys.
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index c5badb9148ea..32827ae11e0b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -44,8 +45,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Predicate;
/**
* Structured description of Intent values to be matched. An IntentFilter can
@@ -147,6 +150,8 @@ import java.util.function.BiConsumer;
* will only match an Intent that does not have any categories.
*/
public class IntentFilter implements Parcelable {
+ private static final String TAG = "IntentFilter";
+
private static final String AGLOB_STR = "aglob";
private static final String SGLOB_STR = "sglob";
private static final String PREFIX_STR = "prefix";
@@ -1761,6 +1766,35 @@ public class IntentFilter implements Parcelable {
}
/**
+ * Return a {@link Predicate} which tests whether this filter matches the
+ * given <var>intent</var>.
+ * <p>
+ * The intent's type will always be tested using a simple
+ * {@link Intent#getType()} check. To instead perform a detailed type
+ * resolution before matching, use
+ * {@link #asPredicateWithTypeResolution(ContentResolver)}.
+ */
+ public @NonNull Predicate<Intent> asPredicate() {
+ return i -> match(null, i, false, TAG) >= 0;
+ }
+
+ /**
+ * Return a {@link Predicate} which tests whether this filter matches the
+ * given <var>intent</var>.
+ * <p>
+ * The intent's type will always be resolved by calling
+ * {@link Intent#resolveType(ContentResolver)} before matching.
+ *
+ * @param resolver to be used when calling
+ * {@link Intent#resolveType(ContentResolver)} before matching.
+ */
+ public @NonNull Predicate<Intent> asPredicateWithTypeResolution(
+ @NonNull ContentResolver resolver) {
+ Objects.requireNonNull(resolver);
+ return i -> match(resolver, i, true, TAG) >= 0;
+ }
+
+ /**
* Test whether this filter matches the given <var>intent</var>.
*
* @param intent The Intent to compare against.
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index a167cb3d851f..8d3452ec2c60 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -597,7 +597,7 @@ public final class PermissionChecker {
* which will evaluate the permission access based on the current fg/bg state of the
* app and leave a record that the data was accessed.
*
- * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
* @param context Context for accessing resources.
@@ -634,7 +634,7 @@ public final class PermissionChecker {
* listener you should use this method which will evaluate the permission access based
* on the current fg/bg state of the app and leave a record that the data was accessed.
*
- * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
* @param context Context for accessing resources.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d3ed00608324..a4ff18a5e85d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -752,6 +752,9 @@ interface IPackageManager {
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
+ IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId);
+
//------------------------------------------------------------------------
//
// The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2ed00b5d2982..c2f3ef0caaf4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4470,12 +4470,17 @@ public abstract class PackageManager {
* main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
* <code>null</code> if neither are found.
*
+ * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+ * the caller is not allowed to query for the <code>packageName</code>.
+ *
* @param packageName The name of the package to inspect.
*
* @return A fully-qualified {@link Intent} that can be used to launch the
* main activity in the package. Returns <code>null</code> if the package
* does not contain such an activity, or if <em>packageName</em> is not
* recognized.
+ *
+ * @see #getLaunchIntentSenderForPackage(String)
*/
public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
@@ -4510,6 +4515,28 @@ public abstract class PackageManager {
public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
/**
+ * Returns an {@link IntentSender} that can be used to launch a front-door activity in a
+ * package. This is used, for example, to implement an "open" button when browsing through
+ * packages. The current implementation is the same with
+ * {@link #getLaunchIntentForPackage(String)}. Instead of returning the {@link Intent}, it
+ * returns the {@link IntentSender} which is not restricted by the package visibility.
+ *
+ * <p>The caller can invoke
+ * {@link IntentSender#sendIntent(Context, int, Intent, IntentSender.OnFinished, Handler)}
+ * to launch the activity. An {@link IntentSender.SendIntentException} is thrown if the
+ * package does not contain such an activity, or if <em>packageName</em> is not recognized.
+ *
+ * @param packageName The name of the package to inspect.
+ * @return Returns a {@link IntentSender} to launch the activity.
+ *
+ * @see #getLaunchIntentForPackage(String)
+ */
+ public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+ throw new UnsupportedOperationException("getLaunchIntentSenderForPackage not implemented"
+ + "in subclass");
+ }
+
+ /**
* Return an array of all of the POSIX secondary group IDs that have been
* assigned to the given package.
* <p>
@@ -6798,7 +6825,7 @@ public abstract class PackageManager {
@NonNull
public Resources getResourcesForApplication(@NonNull ApplicationInfo app, @Nullable
Configuration configuration) throws NameNotFoundException {
- throw new UnsupportedOperationException();
+ return getResourcesForApplication(app);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ff26242dab2..697b72aa24ba 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -884,7 +884,11 @@ public class PackageParser {
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
if (p.mSigningDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
- pi.signingInfo = new SigningInfo(p.mSigningDetails);
+ pi.signingInfo = new SigningInfo(
+ new android.content.pm.SigningDetails(p.mSigningDetails.signatures,
+ p.mSigningDetails.signatureSchemeVersion,
+ p.mSigningDetails.publicKeys,
+ p.mSigningDetails.pastSigningCertificates));
} else {
pi.signingInfo = null;
}
@@ -1399,7 +1403,7 @@ public class PackageParser {
// must use v2 signing scheme
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
- SigningDetails verified;
+ final android.content.pm.SigningDetails verified;
if (skipVerify) {
// systemDir APKs are already trusted, save time by not verifying; since the signature
// is not verified and some system apps can have their V2+ signatures stripped allow
@@ -1414,9 +1418,13 @@ public class PackageParser {
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
- pkg.mSigningDetails = verified;
+ pkg.mSigningDetails = new SigningDetails(verified.getSignatures(),
+ verified.getSignatureSchemeVersion(),
+ verified.getPublicKeys(),
+ verified.getPastSigningCertificates());
} else {
- if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+ if (!Signature.areExactMatch(pkg.mSigningDetails.signatures,
+ verified.getSignatures())) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
apkPath + " has mismatched certificates");
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 691c69c2459a..a5d97f958ea5 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -211,6 +211,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
* Additional flag for {@link #protectionLevel}, corresponding to the
* {@code documenter} value of {@link android.R.attr#protectionLevel}.
*
+ * @deprecated this protectionLevel is obsolete. Permissions previously granted
+ * through this protectionLevel have been migrated to use <code>role</code> instead
* @hide
*/
@SystemApi
@@ -309,7 +311,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_OEM,
PROTECTION_FLAG_VENDOR_PRIVILEGED,
PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
- PROTECTION_FLAG_DOCUMENTER,
PROTECTION_FLAG_CONFIGURATOR,
PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
PROTECTION_FLAG_APP_PREDICTOR,
@@ -561,9 +562,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
protLevel.append("|textClassifier");
}
- if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
- protLevel.append("|documenter");
- }
if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
protLevel.append("|configurator");
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index bce4b872b8a6..3f5c5d21428e 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -256,7 +256,7 @@ public class Signature implements Parcelable {
try {
if (obj != null) {
Signature other = (Signature)obj;
- // Note, some classes, such as PackageParser.SigningDetails, rely on equals
+ // Note, some classes, such as SigningDetails, rely on equals
// only comparing the mSignature arrays without the flags.
return this == other || Arrays.equals(mSignature, other.mSignature);
}
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
new file mode 100644
index 000000000000..584a058aaede
--- /dev/null
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -0,0 +1,928 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+import libcore.util.HexEncoding;
+
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A container for signing-related data of an application package.
+ *
+ * @hide
+ */
+@DataClass(genConstructor = false, genConstDefs = false, genParcelable = true, genAidl = false)
+public final class SigningDetails implements Parcelable {
+
+ private static final String TAG = "SigningDetails";
+
+ @IntDef({SignatureSchemeVersion.UNKNOWN,
+ SignatureSchemeVersion.JAR,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2,
+ SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SignatureSchemeVersion.SIGNING_BLOCK_V4})
+ public @interface SignatureSchemeVersion {
+ int UNKNOWN = 0;
+ int JAR = 1;
+ int SIGNING_BLOCK_V2 = 2;
+ int SIGNING_BLOCK_V3 = 3;
+ int SIGNING_BLOCK_V4 = 4;
+ }
+
+ /** The signing certificates associated with this application package. */
+ private final @Nullable Signature[] mSignatures;
+
+ /** The signature scheme version for this application package. */
+ private final @SignatureSchemeVersion int mSignatureSchemeVersion;
+
+ /** The public keys set for the certificates. */
+ private final @Nullable ArraySet<PublicKey> mPublicKeys;
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ private final @Nullable Signature[] mPastSigningCertificates;
+
+ /** special value used to see if cert is in package - not exposed to callers */
+ private static final int PAST_CERT_EXISTS = 0;
+
+ @IntDef(flag = true,
+ value = {CertCapabilities.INSTALLED_DATA,
+ CertCapabilities.SHARED_USER_ID,
+ CertCapabilities.PERMISSION,
+ CertCapabilities.ROLLBACK})
+ public @interface CertCapabilities {
+
+ /** accept data from already installed pkg with this cert */
+ int INSTALLED_DATA = 1;
+
+ /** accept sharedUserId with pkg with this cert */
+ int SHARED_USER_ID = 2;
+
+ /** grant SIGNATURE permissions to pkgs with this cert */
+ int PERMISSION = 4;
+
+ /** allow pkg to update to one signed by this certificate */
+ int ROLLBACK = 8;
+
+ /** allow pkg to continue to have auth access gated by this cert */
+ int AUTH = 16;
+ }
+
+ /** A representation of unknown signing details. Use instead of null. */
+ public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
+ SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null);
+
+ @VisibleForTesting
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ @Nullable ArraySet<PublicKey> keys, @Nullable Signature[] pastSigningCertificates) {
+ mSignatures = signatures;
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ mPublicKeys = keys;
+ mPastSigningCertificates = pastSigningCertificates;
+ }
+
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ @Nullable Signature[] pastSigningCertificates)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+ pastSigningCertificates);
+ }
+
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, /* pastSigningCertificates */ null);
+ }
+
+ public SigningDetails(@Nullable SigningDetails orig) {
+ if (orig != null) {
+ if (orig.mSignatures != null) {
+ mSignatures = orig.mSignatures.clone();
+ } else {
+ mSignatures = null;
+ }
+ mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
+ mPublicKeys = new ArraySet<>(orig.mPublicKeys);
+ if (orig.mPastSigningCertificates != null) {
+ mPastSigningCertificates = orig.mPastSigningCertificates.clone();
+ } else {
+ mPastSigningCertificates = null;
+ }
+ } else {
+ mSignatures = null;
+ mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ mPublicKeys = null;
+ mPastSigningCertificates = null;
+ }
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage in the provided {@code
+ * otherSigningDetails} when one has the same or an ancestor signer of the other.
+ *
+ * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
+ * containing the longest common lineage with the most restrictive capabilities. If the two
+ * lineages contain the same signers with the same capabilities then the instance on which
+ * this was invoked is returned without any changes. Similarly if neither instance has a
+ * lineage, or if neither has the same or an ancestor signer then this instance is returned.
+ *
+ * Following are some example results of this method for lineages with signers A, B, C, D:
+ * - lineage B merged with lineage A -> B returns lineage A -> B.
+ * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
+ * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
+ * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
+ * lineage A -> B with both capabilities revoked for A.
+ * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
+ * A -> B -> C since the current signer of both instances is not the same or in the
+ * lineage of the other.
+ *
+ * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
+ * of the other. If neither instance has a lineage, or if neither has the same or an
+ * ancestor signer then this instance is returned.
+ */
+ public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
+ if (!hasPastSigningCertificates()) {
+ return otherSigningDetails.hasPastSigningCertificates()
+ && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
+ }
+ if (!otherSigningDetails.hasPastSigningCertificates()) {
+ return this;
+ }
+ // Use the utility method to determine which SigningDetails instance is the descendant
+ // and to confirm that the signing lineage does not diverge.
+ SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails);
+ if (descendantSigningDetails == null) {
+ return this;
+ }
+ return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
+ otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
+ * signer in the provided {@code otherSigningDetails}.
+ *
+ * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @return Merged {@code SigningDetails} instance.
+ */
+ private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
+ @NonNull SigningDetails otherSigningDetails) {
+ // This method should only be called with instances that contain lineages.
+ int index = mPastSigningCertificates.length - 1;
+ int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
+ if (index < 0 || otherIndex < 0) {
+ return this;
+ }
+
+ List<Signature> mergedSignatures = new ArrayList<>();
+ boolean capabilitiesModified = false;
+ // If this is a descendant lineage then add all of the descendant signer(s) to the
+ // merged lineage until the ancestor signer is reached.
+ while (index >= 0 && !mPastSigningCertificates[index].equals(
+ otherSigningDetails.mPastSigningCertificates[otherIndex])) {
+ mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+ }
+ // If the signing lineage was exhausted then the provided ancestor is not actually an
+ // ancestor of this lineage.
+ if (index < 0) {
+ return this;
+ }
+
+ do {
+ // Add the common signer to the merged lineage with the most restrictive
+ // capabilities of the two lineages.
+ Signature signature = mPastSigningCertificates[index--];
+ Signature ancestorSignature =
+ otherSigningDetails.mPastSigningCertificates[otherIndex--];
+ Signature mergedSignature = new Signature(signature);
+ int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
+ if (signature.getFlags() != mergedCapabilities) {
+ capabilitiesModified = true;
+ mergedSignature.setFlags(mergedCapabilities);
+ }
+ mergedSignatures.add(mergedSignature);
+ } while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals(
+ otherSigningDetails.mPastSigningCertificates[otherIndex]));
+
+ // If both lineages still have elements then their lineages have diverged; since this is
+ // not supported return the invoking instance.
+ if (index >= 0 && otherIndex >= 0) {
+ return this;
+ }
+
+ // Add any remaining elements from either lineage that is not yet exhausted to the
+ // the merged lineage.
+ while (otherIndex >= 0) {
+ mergedSignatures.add(new Signature(
+ otherSigningDetails.mPastSigningCertificates[otherIndex--]));
+ }
+ while (index >= 0) {
+ mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+ }
+
+ // if this lineage already contains all the elements in the ancestor and none of the
+ // capabilities were changed then just return this instance.
+ if (mergedSignatures.size() == mPastSigningCertificates.length
+ && !capabilitiesModified) {
+ return this;
+ }
+ // Since the signatures were added to the merged lineage from newest to oldest reverse
+ // the list to ensure the oldest signer is at index 0.
+ Collections.reverse(mergedSignatures);
+ try {
+ return new SigningDetails(new Signature[]{new Signature(mSignatures[0])},
+ mSignatureSchemeVersion, mergedSignatures.toArray(new Signature[0]));
+ } catch (CertificateException e) {
+ Slog.e(TAG, "Caught an exception creating the merged lineage: ", e);
+ return this;
+ }
+ }
+
+ /**
+ * Returns whether this and the provided {@code otherSigningDetails} share a common
+ * ancestor.
+ *
+ * <p>The two SigningDetails have a common ancestor if any of the following conditions are
+ * met:
+ * - If neither has a lineage and their current signer(s) are equal.
+ * - If only one has a lineage and the signer of the other is the same or in the lineage.
+ * - If both have a lineage and their current signers are the same or one is in the lineage
+ * of the other, and their lineages do not diverge to different signers.
+ */
+ public boolean hasCommonAncestor(@NonNull SigningDetails otherSigningDetails) {
+ if (!hasPastSigningCertificates()) {
+ // If this instance does not have a lineage then it must either be in the ancestry
+ // of or the same signer of the otherSigningDetails.
+ return otherSigningDetails.hasAncestorOrSelf(this);
+ }
+ if (!otherSigningDetails.hasPastSigningCertificates()) {
+ return hasAncestorOrSelf(otherSigningDetails);
+ }
+ // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing
+ // details; a null return from that method indicates there is no common lineage between
+ // the two or that they diverge at a point in the lineage.
+ return getDescendantOrSelf(otherSigningDetails) != null;
+ }
+
+ /**
+ * Returns whether this instance is currently signed, or has ever been signed, with a
+ * signing certificate from the provided {@link Set} of {@code certDigests}.
+ *
+ * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding
+ * of each trusted certificate with the digest characters in upper case. If this instance
+ * has multiple signers then all signers must be in the provided {@code Set}. If this
+ * instance has a signing lineage then this method will return true if any of the previous
+ * signers in the lineage match one of the entries in the {@code Set}.
+ */
+ public boolean hasAncestorOrSelfWithDigest(@Nullable Set<String> certDigests) {
+ if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) {
+ return false;
+ }
+ // If an app is signed by multiple signers then all of the signers must be in the Set.
+ if (mSignatures.length > 1) {
+ // If the Set has less elements than the number of signatures then immediately
+ // return false as there's no way to satisfy the requirement of all signatures being
+ // in the Set.
+ if (certDigests.size() < mSignatures.length) {
+ return false;
+ }
+ for (Signature signature : mSignatures) {
+ String signatureDigest = PackageUtils.computeSha256Digest(
+ signature.toByteArray());
+ if (!certDigests.contains(signatureDigest)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String signatureDigest = PackageUtils.computeSha256Digest(mSignatures[0].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // The last element in the pastSigningCertificates array is the current signer;
+ // since that was verified above just check all the signers in the lineage.
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ signatureDigest = PackageUtils.computeSha256Digest(
+ mPastSigningCertificates[i].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the SigningDetails with a descendant (or same) signer after verifying the
+ * descendant has the same, a superset, or a subset of the lineage of the ancestor.
+ *
+ * <p>If this instance and the provided {@code otherSigningDetails} do not share an
+ * ancestry, or if their lineages diverge then null is returned to indicate there is no
+ * valid descendant SigningDetails.
+ */
+ private @Nullable SigningDetails getDescendantOrSelf(
+ @NonNull SigningDetails otherSigningDetails) {
+ final SigningDetails descendantSigningDetails;
+ final SigningDetails ancestorSigningDetails;
+ if (hasAncestorOrSelf(otherSigningDetails)) {
+ // If the otherSigningDetails has the same signer or a signer in the lineage of this
+ // instance then treat this instance as the descendant.
+ descendantSigningDetails = this;
+ ancestorSigningDetails = otherSigningDetails;
+ } else if (otherSigningDetails.hasAncestor(this)) {
+ // The above check confirmed that the two instances do not have the same signer and
+ // the signer of otherSigningDetails is not in this instance's lineage; if this
+ // signer is in the otherSigningDetails lineage then treat this as the ancestor.
+ descendantSigningDetails = otherSigningDetails;
+ ancestorSigningDetails = this;
+ } else {
+ // The signers are not the same and neither has the current signer of the other in
+ // its lineage; return null to indicate there is no descendant signer.
+ return null;
+ }
+ // Once the descent (or same) signer is identified iterate through the ancestry until
+ // the current signer of the ancestor is found.
+ int descendantIndex = descendantSigningDetails.mPastSigningCertificates.length - 1;
+ int ancestorIndex = ancestorSigningDetails.mPastSigningCertificates.length - 1;
+ while (descendantIndex >= 0
+ && !descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+ ancestorSigningDetails.mPastSigningCertificates[ancestorIndex])) {
+ descendantIndex--;
+ }
+ // Since the ancestry was verified above the descendant lineage should never be
+ // exhausted, but if for some reason the ancestor signer is not found then return null.
+ if (descendantIndex < 0) {
+ return null;
+ }
+ // Once the common ancestor (or same) signer is found iterate over the lineage of both
+ // to ensure that they are either the same or one is a subset of the other.
+ do {
+ descendantIndex--;
+ ancestorIndex--;
+ } while (descendantIndex >= 0 && ancestorIndex >= 0
+ && descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+ ancestorSigningDetails.mPastSigningCertificates[ancestorIndex]));
+
+ // If both lineages still have elements then they diverge and cannot be considered a
+ // valid common lineage.
+ if (descendantIndex >= 0 && ancestorIndex >= 0) {
+ return null;
+ }
+ // Since one or both of the lineages was exhausted they are either the same or one is a
+ // subset of the other; return the valid descendant.
+ return descendantSigningDetails;
+ }
+
+ /** Returns true if the signing details have one or more signatures. */
+ public boolean hasSignatures() {
+ return mSignatures != null && mSignatures.length > 0;
+ }
+
+ /** Returns true if the signing details have past signing certificates. */
+ public boolean hasPastSigningCertificates() {
+ return mPastSigningCertificates != null && mPastSigningCertificates.length > 0;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+ * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+ * then that means it has authorized a signing certificate rotation, which eventually leads
+ * to our certificate, and thus can be trusted. If this method evaluates to true, this
+ * SigningDetails object should be trusted if the previous one is.
+ */
+ public boolean hasAncestorOrSelf(@NonNull SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.mSignatures.length > 1) {
+ // multiple-signer packages cannot rotate signing certs, so we just compare current
+ // signers for an exact match
+ return signaturesMatchExactly(oldDetails);
+ } else {
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates
+ return hasCertificate(oldDetails.mSignatures[0]);
+ }
+ }
+
+ /**
+ * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails}
+ * is a descendant of {@code oldDetails}, not if they're the same. This is used to
+ * determine if this object is newer than the provided one.
+ */
+ public boolean hasAncestor(@NonNull SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+ // the last entry in pastSigningCertificates is the current signer, ignore it
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (mPastSigningCertificates[i].equals(oldDetails.mSignatures[0])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether this {@code SigningDetails} has a signer in common with the provided
+ * {@code otherDetails} with the specified {@code flags} capabilities provided by this
+ * signer.
+ *
+ * <p>Note this method allows for the signing lineage to diverge, so this should only be
+ * used for instances where the only requirement is a common signer in the lineage with
+ * the specified capabilities. If the current signer of this instance is an ancestor of
+ * {@code otherDetails} then {@code true} is immediately returned since the current signer
+ * has all capabilities granted.
+ */
+ public boolean hasCommonSignerWithCapability(@NonNull SigningDetails otherDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || otherDetails == UNKNOWN) {
+ return false;
+ }
+ // If either is signed with more than one signer then both must be signed by the same
+ // signers to consider the capabilities granted.
+ if (mSignatures.length > 1 || otherDetails.mSignatures.length > 1) {
+ return signaturesMatchExactly(otherDetails);
+ }
+ // The Signature class does not use the granted capabilities in the hashCode
+ // computation, so a Set can be used to check for a common signer.
+ Set<Signature> otherSignatures = new ArraySet<>();
+ if (otherDetails.hasPastSigningCertificates()) {
+ otherSignatures.addAll(Arrays.asList(otherDetails.mPastSigningCertificates));
+ } else {
+ otherSignatures.addAll(Arrays.asList(otherDetails.mSignatures));
+ }
+ // If the current signer of this instance is an ancestor of the other than return true
+ // since all capabilities are granted to the current signer.
+ if (otherSignatures.contains(mSignatures[0])) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // Since the current signer was checked above and the last signature in the
+ // pastSigningCertificates is the current signer skip checking the last element.
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (otherSignatures.contains(mPastSigningCertificates[i])) {
+ // If the caller specified multiple capabilities ensure all are set.
+ if ((mPastSigningCertificates[i].getFlags() & flags) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+ * not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ */
+ public boolean checkCapability(@NonNull SigningDetails oldDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.mSignatures.length > 1) {
+ // multiple-signer packages cannot rotate signing certs, so we must have an exact
+ // match, which also means all capabilities are granted
+ return signaturesMatchExactly(oldDetails);
+ } else {
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates, and if we grant it the capability it's
+ // requesting
+ return hasCertificate(oldDetails.mSignatures[0], flags);
+ }
+ }
+
+ /**
+ * A special case of {@code checkCapability} which re-encodes both sets of signing
+ * certificates to counteract a previous re-encoding.
+ */
+ public boolean checkCapabilityRecover(@NonNull SigningDetails oldDetails,
+ @CertCapabilities int flags) throws CertificateException {
+ if (oldDetails == UNKNOWN || this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+ // signing certificates may have rotated, check entire history for effective match
+ for (int i = 0; i < mPastSigningCertificates.length; i++) {
+ if (Signature.areEffectiveMatch(
+ oldDetails.mSignatures[0],
+ mPastSigningCertificates[i])
+ && mPastSigningCertificates[i].getFlags() == flags) {
+ return true;
+ }
+ }
+ } else {
+ return Signature.areEffectiveMatch(oldDetails.mSignatures, mSignatures);
+ }
+ return false;
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer. Automatically returns false if this object has multiple
+ * signing certificates, since rotation is only supported for single-signers; this is
+ * enforced by {@code hasCertificateInternal}.
+ */
+ public boolean hasCertificate(@NonNull Signature signature) {
+ return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer, and whether or not it has the given permission.
+ * Certificates which match our current signer automatically get all capabilities.
+ * Automatically returns false if this object has multiple signing certificates, since
+ * rotation is only supported for single-signers.
+ */
+ public boolean hasCertificate(@NonNull Signature signature, @CertCapabilities int flags) {
+ return hasCertificateInternal(signature, flags);
+ }
+
+ /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+ public boolean hasCertificate(byte[] certificate) {
+ Signature signature = new Signature(certificate);
+ return hasCertificate(signature);
+ }
+
+ private boolean hasCertificateInternal(@NonNull Signature signature, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // only single-signed apps can have pastSigningCertificates
+ if (hasPastSigningCertificates()) {
+ // check all past certs, except for the current one, which automatically gets all
+ // capabilities, since it is the same as the current signature
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (mPastSigningCertificates[i].equals(signature)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer and make sure
+ // we are singly-signed
+ return mSignatures.length == 1 && mSignatures[0].equals(signature);
+ }
+
+ /**
+ * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+ * or not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ *
+ * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an
+ * app with multiple signers, this represents the hex-encoded sha256
+ * digest of the combined hex-encoded sha256 digests of each individual
+ * signing certificate according to {@link
+ * PackageUtils#computeSignaturesSha256Digest(Signature[])}
+ */
+ public boolean checkCapability(@Nullable String sha256String, @CertCapabilities int flags) {
+ if (this == UNKNOWN || TextUtils.isEmpty(sha256String)) {
+ return false;
+ }
+
+ // first see if the hash represents a single-signer in our signing history
+ final byte[] sha256Bytes = HexEncoding.decode(sha256String, false /* allowSingleChar */);
+ if (hasSha256Certificate(sha256Bytes, flags)) {
+ return true;
+ }
+
+ // Not in signing history, either represents multiple signatures or not a match.
+ // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+ // We already check the single-signer case above as part of hasSha256Certificate, so no
+ // need to verify we have multiple signers, just run the old check
+ // just consider current signing certs
+ final String[] mSignaturesSha256Digests =
+ PackageUtils.computeSignaturesSha256Digests(mSignatures);
+ final String mSignaturesSha256Digest =
+ PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+ return mSignaturesSha256Digest.equals(sha256String);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+ * history, including the current signer. Automatically returns false if this object has
+ * multiple signing certificates, since rotation is only supported for single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate) {
+ return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+ * certificate in this SigningDetails' signing certificate history, including the current
+ * signer, and whether or not it has the given permission. Certificates which match our
+ * current signer automatically get all capabilities. Automatically returns false if this
+ * object has multiple signing certificates, since rotation is only supported for
+ * single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+ return hasSha256CertificateInternal(sha256Certificate, flags);
+ }
+
+ private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates()) {
+ // check all past certs, except for the last one, which automatically gets all
+ // capabilities, since it is the same as the current signature, and is checked below
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ mPastSigningCertificates[i].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer
+ if (mSignatures.length == 1) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(mSignatures[0].toByteArray());
+ return Arrays.equals(sha256Certificate, digest);
+ }
+ return false;
+ }
+
+ /** Returns true if the signatures in this and other match exactly. */
+ public boolean signaturesMatchExactly(@NonNull SigningDetails other) {
+ return Signature.areExactMatch(mSignatures, other.mSignatures);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ boolean isUnknown = UNKNOWN == this;
+ dest.writeBoolean(isUnknown);
+ if (isUnknown) {
+ return;
+ }
+ dest.writeTypedArray(mSignatures, flags);
+ dest.writeInt(mSignatureSchemeVersion);
+ dest.writeArraySet(mPublicKeys);
+ dest.writeTypedArray(mPastSigningCertificates, flags);
+ }
+
+ protected SigningDetails(@NonNull Parcel in) {
+ final ClassLoader boot = Object.class.getClassLoader();
+ mSignatures = in.createTypedArray(Signature.CREATOR);
+ mSignatureSchemeVersion = in.readInt();
+ mPublicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ mPastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+ }
+
+ public static final @NonNull Parcelable.Creator<SigningDetails> CREATOR =
+ new Creator<SigningDetails>() {
+ @Override
+ public SigningDetails createFromParcel(@NonNull Parcel source) {
+ if (source.readBoolean()) {
+ return UNKNOWN;
+ }
+ return new SigningDetails(source);
+ }
+
+ @Override
+ public SigningDetails[] newArray(int size) {
+ return new SigningDetails[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SigningDetails)) return false;
+
+ final SigningDetails that = (SigningDetails) o;
+
+ if (mSignatureSchemeVersion != that.mSignatureSchemeVersion) return false;
+ if (!Signature.areExactMatch(mSignatures, that.mSignatures)) return false;
+ if (mPublicKeys != null) {
+ if (!mPublicKeys.equals((that.mPublicKeys))) {
+ return false;
+ }
+ } else if (that.mPublicKeys != null) {
+ return false;
+ }
+
+ // can't use Signature.areExactMatch() because order matters with the past signing certs
+ if (!Arrays.equals(mPastSigningCertificates, that.mPastSigningCertificates)) {
+ return false;
+ }
+ // The capabilities for the past signing certs must match as well.
+ for (int i = 0; i < mPastSigningCertificates.length; i++) {
+ if (mPastSigningCertificates[i].getFlags()
+ != that.mPastSigningCertificates[i].getFlags()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = +Arrays.hashCode(mSignatures);
+ result = 31 * result + mSignatureSchemeVersion;
+ result = 31 * result + (mPublicKeys != null ? mPublicKeys.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(mPastSigningCertificates);
+ return result;
+ }
+
+ /**
+ * Builder of {@code SigningDetails} instances.
+ */
+ public static class Builder {
+ private @NonNull Signature[] mSignatures;
+ private @SignatureSchemeVersion int mSignatureSchemeVersion =
+ SignatureSchemeVersion.UNKNOWN;
+ private @Nullable Signature[] mPastSigningCertificates;
+
+ public Builder() {
+ }
+
+ /** get signing certificates used to sign the current APK */
+ public SigningDetails.Builder setSignatures(@NonNull Signature[] signatures) {
+ mSignatures = signatures;
+ return this;
+ }
+
+ /** set the signature scheme version used to sign the APK */
+ public SigningDetails.Builder setSignatureSchemeVersion(
+ @SignatureSchemeVersion int signatureSchemeVersion) {
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ return this;
+ }
+
+ /** set the signing certificates by which the APK proved it can be authenticated */
+ public SigningDetails.Builder setPastSigningCertificates(
+ @Nullable Signature[] pastSigningCertificates) {
+ mPastSigningCertificates = pastSigningCertificates;
+ return this;
+ }
+
+ private void checkInvariants() {
+ // must have signatures and scheme version set
+ if (mSignatures == null) {
+ throw new IllegalStateException("SigningDetails requires the current signing"
+ + " certificates.");
+ }
+ }
+ /** build a {@code SigningDetails} object */
+ public SigningDetails build()
+ throws CertificateException {
+ checkInvariants();
+ return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+ mPastSigningCertificates);
+ }
+ }
+
+ /** Parses the public keys from the set of signatures. */
+ public static ArraySet<PublicKey> toSigningKeys(@NonNull Signature[] signatures)
+ throws CertificateException {
+ final ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+ for (int i = 0; i < signatures.length; i++) {
+ keys.add(signatures[i].getPublicKey());
+ }
+ return keys;
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/SigningDetails.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * The signing certificates associated with this application package.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Signature[] getSignatures() {
+ return mSignatures;
+ }
+
+ /**
+ * The signature scheme version for this application package.
+ */
+ @DataClass.Generated.Member
+ public @SignatureSchemeVersion int getSignatureSchemeVersion() {
+ return mSignatureSchemeVersion;
+ }
+
+ /**
+ * The public keys set for the certificates.
+ */
+ @DataClass.Generated.Member
+ public @Nullable ArraySet<PublicKey> getPublicKeys() {
+ return mPublicKeys;
+ }
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ @DataClass.Generated.Member
+ public @Nullable Signature[] getPastSigningCertificates() {
+ return mPastSigningCertificates;
+ }
+
+ @DataClass.Generated(
+ time = 1616984092921L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java",
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index d14be9c6b734..7459a9029212 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,25 +26,25 @@ import android.os.Parcelable;
public final class SigningInfo implements Parcelable {
@NonNull
- private final PackageParser.SigningDetails mSigningDetails;
+ private final SigningDetails mSigningDetails;
public SigningInfo() {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
/**
* @hide only packagemanager should be populating this
*/
- public SigningInfo(PackageParser.SigningDetails signingDetails) {
- mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+ public SigningInfo(SigningDetails signingDetails) {
+ mSigningDetails = new SigningDetails(signingDetails);
}
public SigningInfo(SigningInfo orig) {
- mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ mSigningDetails = new SigningDetails(orig.mSigningDetails);
}
private SigningInfo(Parcel source) {
- mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+ mSigningDetails = SigningDetails.CREATOR.createFromParcel(source);
}
/**
@@ -53,7 +52,8 @@ public final class SigningInfo implements Parcelable {
* their identity is viewed as being the set of all signers, not just any one.
*/
public boolean hasMultipleSigners() {
- return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+ return mSigningDetails.getSignatures() != null
+ && mSigningDetails.getSignatures().length > 1;
}
/**
@@ -65,8 +65,8 @@ public final class SigningInfo implements Parcelable {
* signing history, since it could change to a new signing certificate at any time.
*/
public boolean hasPastSigningCertificates() {
- return mSigningDetails.signatures != null
- && mSigningDetails.pastSigningCertificates != null;
+ return mSigningDetails.getPastSigningCertificates() != null
+ && mSigningDetails.getPastSigningCertificates().length > 0;
}
/**
@@ -93,11 +93,11 @@ public final class SigningInfo implements Parcelable {
} else if (!hasPastSigningCertificates()) {
// this package is only signed by one signer with no history, return it
- return mSigningDetails.signatures;
+ return mSigningDetails.getSignatures();
} else {
// this package has provided proof of past signing certificates, include them
- return mSigningDetails.pastSigningCertificates;
+ return mSigningDetails.getPastSigningCertificates();
}
}
@@ -111,7 +111,7 @@ public final class SigningInfo implements Parcelable {
* </note>
*/
public Signature[] getApkContentsSigners() {
- return mSigningDetails.signatures;
+ return mSigningDetails.getSignatures();
}
@Override
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index d8ec512d9f1e..024c18c30d37 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -19,7 +19,7 @@ package android.content.pm.parsing;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import com.android.internal.util.DataClass;
@@ -398,10 +398,10 @@ public class ApkLite {
}
@DataClass.Generated(
- time = 1610596637723L,
+ time = 1616985847981L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.PackageParser.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 154d9234d00c..01c0a888647c 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -28,6 +28,7 @@ import android.annotation.NonNull;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
@@ -301,16 +302,15 @@ public class ApkLiteParseUtils {
parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- ParseResult<PackageParser.SigningDetails> result =
- ParsingPackageUtils.getSigningDetails(input,
- apkFile.getAbsolutePath(), skipVerify, false,
- PackageParser.SigningDetails.UNKNOWN,
- DEFAULT_TARGET_SDK_VERSION);
+ final ParseResult<SigningDetails> result =
+ ParsingPackageUtils.getSigningDetails(input, apkFile.getAbsolutePath(),
+ skipVerify, /* isStaticSharedLibrary */ false,
+ SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
if (result.isError()) {
return input.error(result);
}
@@ -319,7 +319,7 @@ public class ApkLiteParseUtils {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
- signingDetails = PackageParser.SigningDetails.UNKNOWN;
+ signingDetails = SigningDetails.UNKNOWN;
}
return parseApkLite(input, apkPath, parser, signingDetails);
@@ -340,7 +340,7 @@ public class ApkLiteParseUtils {
}
private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
- XmlResourceParser parser, PackageParser.SigningDetails signingDetails)
+ XmlResourceParser parser, SigningDetails signingDetails)
throws IOException, XmlPullParserException {
ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
if (result.isError()) {
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index c9054fd8976d..4ccd67dd9003 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -32,7 +32,6 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -40,6 +39,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ComponentParseUtils;
@@ -330,26 +330,26 @@ public class PackageInfoWithoutStateUtils {
pi.isApex = true;
}
- PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+ final SigningDetails signingDetails = pkg.getSigningDetails();
// deprecated method of getting signing certificates
if ((flags & PackageManager.GET_SIGNATURES) != 0) {
if (signingDetails.hasPastSigningCertificates()) {
// Package has included signing certificate rotation information. Return the oldest
// cert so that programmatic checks keep working even if unaware of key rotation.
pi.signatures = new Signature[1];
- pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+ pi.signatures[0] = signingDetails.getPastSigningCertificates()[0];
} else if (signingDetails.hasSignatures()) {
// otherwise keep old behavior
- int numberOfSigs = signingDetails.signatures.length;
+ int numberOfSigs = signingDetails.getSignatures().length;
pi.signatures = new Signature[numberOfSigs];
- System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+ System.arraycopy(signingDetails.getSignatures(), 0, pi.signatures, 0,
numberOfSigs);
}
}
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
- if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ if (signingDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
pi.signingInfo = new SigningInfo(signingDetails);
} else {
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index ed68dbf0087c..72cc9299376a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -25,7 +25,7 @@ import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -320,7 +320,7 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage setSharedUserLabel(int sharedUserLabel);
- ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+ ParsingPackage setSigningDetails(SigningDetails signingDetails);
ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 5a7f21040d0a..34a57f33ebf9 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -32,7 +32,7 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedComponent;
@@ -292,7 +292,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@DataClass.ParcelWith(ForInternedString.class)
protected String volumeUuid;
@Nullable
- private PackageParser.SigningDetails signingDetails;
+ private SigningDetails signingDetails;
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
@@ -723,6 +723,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@Override
public ParsingPackageImpl addImplicitPermission(String permission) {
+ addUsesPermission(new ParsedUsesPermission(permission, 0 /*usesPermissionFlags*/));
this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
TextUtils.safeIntern(permission));
return this;
@@ -1714,7 +1715,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@Nullable
@Override
- public PackageParser.SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return signingDetails;
}
@@ -2276,7 +2277,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
- public ParsingPackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
signingDetails = value;
return this;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index a6e189d32b54..d5bd3a93bcbd 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -26,8 +26,8 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
import android.content.pm.ServiceInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -768,7 +768,7 @@ public interface ParsingPackageRead extends Parcelable {
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
*/
- PackageParser.SigningDetails getSigningDetails();
+ SigningDetails getSigningDetails();
/**
* @see ApplicationInfo#splitClassLoaderNames
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index dce242c9d87c..e96a73342dee 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -47,8 +47,8 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityUtils;
@@ -74,6 +74,7 @@ import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
@@ -905,7 +906,7 @@ public class ParsingPackageUtils {
);
}
- convertNewPermissions(pkg);
+ convertCompatPermissions(pkg);
convertSplitPermissions(pkg);
@@ -2791,31 +2792,16 @@ public class ParsingPackageUtils {
}
}
- private static void convertNewPermissions(ParsingPackage pkg) {
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- StringBuilder newPermsMsg = null;
- for (int ip = 0; ip < NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
+ private static void convertCompatPermissions(ParsingPackage pkg) {
+ for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+ final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+ if (pkg.getTargetSdkVersion() >= info.sdkVersion) {
break;
}
- if (!pkg.getRequestedPermissions().contains(npi.name)) {
- if (newPermsMsg == null) {
- newPermsMsg = new StringBuilder(128);
- newPermsMsg.append(pkg.getPackageName());
- newPermsMsg.append(": compat added ");
- } else {
- newPermsMsg.append(' ');
- }
- newPermsMsg.append(npi.name);
- pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0))
- .addImplicitPermission(npi.name);
+ if (!pkg.getRequestedPermissions().contains(info.name)) {
+ pkg.addImplicitPermission(info.name);
}
}
- if (newPermsMsg != null) {
- Slog.i(TAG, newPermsMsg.toString());
- }
}
private void convertSplitPermissions(ParsingPackage pkg) {
@@ -2831,8 +2817,7 @@ public class ParsingPackageUtils {
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!requestedPermissions.contains(perm)) {
- pkg.addUsesPermission(new ParsedUsesPermission(perm, 0))
- .addImplicitPermission(perm);
+ pkg.addImplicitPermission(perm);
}
}
}
@@ -3057,7 +3042,8 @@ public class ParsingPackageUtils {
if (existingSigningDetails == SigningDetails.UNKNOWN) {
return input.success(verified);
} else {
- if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+ if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
+ verified.getSignatures())) {
return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 7ac78b7c6a24..324612da2846 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -119,7 +119,6 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
// how many APKs they're going through.
mDeferredErrors.erase();
}
- mPackageName = null;
mTargetSdkVersion = null;
return this;
}
diff --git a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
new file mode 100644
index 000000000000..9198b95a6f71
--- /dev/null
+++ b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
@@ -0,0 +1,57 @@
+/*
+ * 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.content.pm.permission;
+
+import android.Manifest;
+import android.content.pm.parsing.component.ParsedUsesPermission;
+
+/**
+ * Implements compatibility support for permissions, and old applications
+ * will be automatically granted it.
+ *
+ * Compatibility permissions are permissions that are automatically granted to
+ * packages that target an SDK prior to when the permission was introduced.
+ * Sometimes the platform makes breaking behaviour changes and hides the legacy
+ * behaviour behind a permission. In these instances, we ensure applications
+ * targeting older platform versions are implicitly granted the correct set of
+ * permissions.
+ *
+ * @hide
+ */
+public class CompatibilityPermissionInfo extends ParsedUsesPermission {
+
+ public final int sdkVersion;
+
+ /**
+ * List of new permissions that have been added since 1.0.
+ *
+ * @hide
+ */
+ public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
+ new CompatibilityPermissionInfo[]{
+ new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
+ new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
+ android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/)
+ };
+
+ private CompatibilityPermissionInfo(String name, int sdkVersion,
+ @UsesPermissionFlags int usesPermissionFlags) {
+ super(name, usesPermissionFlags);
+ this.sdkVersion = sdkVersion;
+ }
+}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 572a8a81883a..ad4e64b9d7a7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -506,6 +506,7 @@ public abstract class SensorManager {
if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION
|| type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE
|| type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE
+ || type == Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT
|| type == Sensor.TYPE_WRIST_TILT_GESTURE
|| type == Sensor.TYPE_DYNAMIC_SENSOR_META || type == Sensor.TYPE_HINGE_ANGLE) {
wakeUpSensor = true;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ecfc0d582b1c..a3be415b1755 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -346,7 +346,7 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecControl {}
- // -- Supported HDM-CEC versions.
+ // -- Supported HDMI-CEC versions.
/**
* Version constant for HDMI-CEC v1.4b.
*
@@ -371,23 +371,67 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecVersion {}
+ // -- Whether the Routing Control feature is enabled or disabled.
+ /**
+ * Routing Control feature enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROUTING_CONTROL_ENABLED = 1;
+ /**
+ * Routing Control feature disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROUTING_CONTROL_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "ROUTING_CONTROL_" }, value = {
+ ROUTING_CONTROL_ENABLED,
+ ROUTING_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoutingControl {}
+
// -- Scope of CEC power control messages sent by a playback device.
/**
- * Send CEC power control messages to TV only.
+ * Send CEC power control messages to TV only:
+ * Upon going to sleep, send {@code <Standby>} to TV only.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} but do not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@SystemApi
public static final String POWER_CONTROL_MODE_TV = "to_tv";
/**
- * Broadcast CEC power control messages to all devices in the network.
+ * Send CEC power control messages to TV and Audio System:
+ * Upon going to sleep, send {@code <Standby>} to TV and Audio system.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+ * the Audio system via {@code <System Audio Mode Request>}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
+ /**
+ * Broadcast CEC power control messages to all devices in the network:
+ * Upon going to sleep, send {@code <Standby>} to all devices in the network.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+ * the Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@SystemApi
public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
/**
- * Don't send any CEC power control messages.
+ * Don't send any CEC power control messages:
+ * Upon going to sleep, do not send any {@code <Standby>} message.
+ * Upon waking up, do not turn on the TV via {@code <One Touch Play>} and do not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@@ -398,6 +442,7 @@ public final class HdmiControlManager {
*/
@StringDef(prefix = { "POWER_CONTROL_MODE_" }, value = {
POWER_CONTROL_MODE_TV,
+ POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
POWER_CONTROL_MODE_BROADCAST,
POWER_CONTROL_MODE_NONE
})
@@ -429,6 +474,31 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface ActiveSourceLostBehavior {}
+ // -- Whether System Audio Control is enabled or disabled.
+ /**
+ * System Audio Control enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1;
+ /**
+ * System Audio Control disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "SYSTEM_AUDIO_CONTROL_" }, value = {
+ SYSTEM_AUDIO_CONTROL_ENABLED,
+ SYSTEM_AUDIO_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemAudioControl {}
+
// -- Whether System Audio Mode muting is enabled or disabled.
/**
* System Audio Mode muting enabled.
@@ -710,6 +780,13 @@ public final class HdmiControlManager {
@SystemApi
public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
/**
+ * Name of a setting deciding whether the Routing Control feature is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+ /**
* Name of a setting deciding on the power control mode.
*
* @hide
@@ -725,6 +802,14 @@ public final class HdmiControlManager {
public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
"power_state_change_on_active_source_lost";
/**
+ * Name of a setting deciding whether System Audio Control is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL =
+ "system_audio_control";
+ /**
* Name of a setting deciding whether System Audio Muting is allowed.
*
* @hide
@@ -778,7 +863,7 @@ public final class HdmiControlManager {
public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
"tv_wake_on_one_touch_play";
/**
- * Name of a setting deciding whether the device will also turn off other CEC devices
+ * Name of a setting deciding whether the TV will also turn off other CEC devices
* when it goes to standby mode.
*
* @hide
@@ -842,6 +927,7 @@ public final class HdmiControlManager {
CEC_SETTING_NAME_HDMI_CEC_VERSION,
CEC_SETTING_NAME_POWER_CONTROL_MODE,
CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -2021,6 +2107,56 @@ public final class HdmiControlManager {
}
/**
+ * Set the status of Routing Control feature.
+ *
+ * <p>This allows to enable/disable Routing Control on the device.
+ * If enabled, the switch device will route to the correct input source on
+ * receiving Routing Control related messages. If disabled, you can only
+ * switch the input via controls on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setRoutingControl(@NonNull @RoutingControl int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of Routing Control feature.
+ *
+ * <p>Reflects whether Routing Control is currently enabled on the device.
+ * If enabled, the switch device will route to the correct input source on
+ * receiving Routing Control related messages. If disabled, you can only
+ * switch the input via controls on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RoutingControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getRoutingControl() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the status of Power Control.
*
* <p>Specifies to which devices Power Control messages should be sent:
@@ -2114,6 +2250,58 @@ public final class HdmiControlManager {
}
/**
+ * Set the current status of System Audio Control.
+ *
+ * <p>Sets whether HDMI System Audio Control feature is enabled. If enabled,
+ * TV or Audio System will try to turn on the System Audio Mode if there's a
+ * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+ * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+ * System Audio Mode will never be activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSystemAudioControl(@NonNull @SystemAudioControl int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of System Audio Control.
+ *
+ * <p>Reflects whether HDMI System Audio Control feature is enabled. If enabled,
+ * TV or Audio System will try to turn on the System Audio Mode if there's a
+ * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+ * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+ * System Audio Mode will never be activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @SystemAudioControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getSystemAudioControl() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the current status of System Audio Mode muting.
*
* <p>Sets whether the device should be muted when System Audio Mode is turned off.
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 60d43fd077d0..861e4409b014 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -3,5 +3,4 @@
include /services/core/java/com/android/server/display/OWNERS
marvinramin@google.com
-nchalko@google.com
lcnathalie@google.com
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 17116e248646..e5d86204077b 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -435,7 +435,7 @@ public final class InputManager {
/**
* Enables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -454,7 +454,7 @@ public final class InputManager {
/**
* Disables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -831,7 +831,7 @@ public final class InputManager {
* Sets the TouchCalibration to apply to the specified input device's coordinates.
* <p>
* This method may have the side-effect of causing the input device in question
- * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+ * to be reconfigured. Requires {@link android.Manifest.permission.SET_INPUT_CALIBRATION}.
* </p>
*
* @param inputDeviceDescriptor The input device descriptor.
@@ -874,7 +874,7 @@ public final class InputManager {
/**
* Sets the mouse pointer speed.
* <p>
- * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
+ * Requires {@link android.Manifest.permission.WRITE_SETTINGS}.
* </p>
*
* @param context The application context.
@@ -1285,7 +1285,7 @@ public final class InputManager {
* @param inputPort The port of the input device.
* @param displayPort The physical port of the associated display.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1302,7 +1302,7 @@ public final class InputManager {
* static association for the cleared input port will be restored.
* @param inputPort The port of the input device to be cleared.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1320,7 +1320,7 @@ public final class InputManager {
* @param inputDeviceName The name of the input device.
* @param displayUniqueId The unique id of the associated display.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1337,7 +1337,7 @@ public final class InputManager {
* Removes a runtime association between the input device and display.
* @param inputDeviceName The name of the input device.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index a5c9a7fafbd8..ad0dc0965b0a 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -19,19 +19,19 @@ package android.hardware.soundtrigger;
import android.annotation.Nullable;
import android.media.AudioFormat;
import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
@@ -43,7 +43,7 @@ import java.util.UUID;
class ConversionUtil {
public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
SoundTriggerModuleDescriptor aidlDesc) {
- SoundTriggerModuleProperties properties = aidlDesc.properties;
+ Properties properties = aidlDesc.properties;
return new SoundTrigger.ModuleProperties(
aidlDesc.handle,
properties.implementor,
@@ -194,19 +194,19 @@ class ConversionUtil {
}
public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
- int modelHandle, RecognitionEvent aidlEvent) {
+ int modelHandle, int captureSession, RecognitionEvent aidlEvent) {
// The API recognition event doesn't allow for a null audio format, even though it doesn't
// always make sense. We thus replace it with a default.
AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig);
return new SoundTrigger.GenericRecognitionEvent(
aidlEvent.status,
- modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
+ modelHandle, aidlEvent.captureAvailable, captureSession,
aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
audioFormat, aidlEvent.data);
}
public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
- int modelHandle,
+ int modelHandle, int captureSession,
PhraseRecognitionEvent aidlEvent) {
SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
@@ -218,7 +218,7 @@ class ConversionUtil {
AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig);
return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
aidlEvent.common.captureAvailable,
- aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
+ captureSession, aidlEvent.common.captureDelayMs,
aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
audioFormat, aidlEvent.common.data,
apiExtras);
@@ -328,9 +328,9 @@ class ConversionUtil {
public static int api2aidlModelParameter(int apiParam) {
switch (apiParam) {
case ModelParams.THRESHOLD_FACTOR:
- return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
+ return android.media.soundtrigger.ModelParameter.THRESHOLD_FACTOR;
default:
- return android.media.soundtrigger_middleware.ModelParameter.INVALID;
+ return android.media.soundtrigger.ModelParameter.INVALID;
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 11f3e4582ac2..163e6f0a0114 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -19,6 +19,7 @@ package android.hardware.soundtrigger;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
+import static android.system.OsConstants.EBUSY;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOSYS;
@@ -41,9 +42,9 @@ import android.media.AudioFormat;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -91,6 +92,8 @@ public class SoundTrigger {
public static final int STATUS_DEAD_OBJECT = -EPIPE;
/** @hide */
public static final int STATUS_INVALID_OPERATION = -ENOSYS;
+ /** @hide */
+ public static final int STATUS_BUSY = -EBUSY;
/*****************************************************************************
* A ModuleProperties describes a given sound trigger hardware module
@@ -1835,120 +1838,6 @@ public class SoundTrigger {
}
}
- /**
- * Status codes for {@link SoundModelEvent}
- */
- /**
- * Sound Model was updated
- *
- * @hide
- */
- public static final int SOUNDMODEL_STATUS_UPDATED = 0;
-
- /**
- * A SoundModelEvent is provided by the
- * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
- * callback when a sound model has been updated by the implementation
- *
- * @hide
- */
- public static class SoundModelEvent implements Parcelable {
- /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
- public final int status;
- /** The updated sound model handle */
- public final int soundModelHandle;
- /** New sound model data */
- @NonNull
- public final byte[] data;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
- this.status = status;
- this.soundModelHandle = soundModelHandle;
- this.data = data != null ? data : new byte[0];
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
- = new Parcelable.Creator<SoundModelEvent>() {
- public SoundModelEvent createFromParcel(Parcel in) {
- return SoundModelEvent.fromParcel(in);
- }
-
- public SoundModelEvent[] newArray(int size) {
- return new SoundModelEvent[size];
- }
- };
-
- private static SoundModelEvent fromParcel(Parcel in) {
- int status = in.readInt();
- int soundModelHandle = in.readInt();
- byte[] data = in.readBlob();
- return new SoundModelEvent(status, soundModelHandle, data);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(status);
- dest.writeInt(soundModelHandle);
- dest.writeBlob(data);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.hashCode(data);
- result = prime * result + soundModelHandle;
- result = prime * result + status;
- return result;
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- SoundModelEvent other = (SoundModelEvent) obj;
- if (!Arrays.equals(data, other.data))
- return false;
- if (soundModelHandle != other.soundModelHandle)
- return false;
- if (status != other.status)
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
- + ", data=" + (data == null ? 0 : data.length) + "]";
- }
- }
-
- /**
- * Native service state. {@link StatusListener#onServiceStateChange(int)}
- */
- // Keep in sync with system/core/include/system/sound_trigger.h
- /**
- * Sound trigger service is enabled
- *
- * @hide
- */
- public static final int SERVICE_STATE_ENABLED = 0;
- /**
- * Sound trigger service is disabled
- *
- * @hide
- */
- public static final int SERVICE_STATE_DISABLED = 1;
private static Object mServiceLock = new Object();
private static ISoundTriggerMiddlewareService mService;
@@ -1975,6 +1864,8 @@ public class SoundTrigger {
return STATUS_DEAD_OBJECT;
case Status.INTERNAL_ERROR:
return STATUS_ERROR;
+ case Status.RESOURCE_CONTENTION:
+ return STATUS_BUSY;
}
return STATUS_ERROR;
}
@@ -2224,27 +2115,28 @@ public class SoundTrigger {
*
* @hide
*/
- public static interface StatusListener {
+ public interface StatusListener {
/**
* Called when recognition succeeds of fails
*/
- public abstract void onRecognition(RecognitionEvent event);
+ void onRecognition(RecognitionEvent event);
/**
- * Called when a sound model has been updated
+ * Called when a sound model has been preemptively unloaded by the underlying
+ * implementation.
*/
- public abstract void onSoundModelUpdate(SoundModelEvent event);
+ void onModelUnloaded(int modelHandle);
/**
- * Called when the sound trigger native service state changes.
- * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
- * {@link SoundTrigger#SERVICE_STATE_DISABLED}
+ * Called whenever underlying conditions change, such that load/start operations that have
+ * previously failed or got preempted may now succeed. This is not a guarantee, merely a
+ * hint that the client may want to retry operations.
*/
- public abstract void onServiceStateChange(int state);
+ void onResourcesAvailable();
/**
* Called when the sound trigger native service dies
*/
- public abstract void onServiceDied();
+ void onServiceDied();
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 431c99dbdc1f..bf4b51431c6f 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -22,13 +22,13 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -48,7 +48,8 @@ public class SoundTriggerModule {
private static final int EVENT_RECOGNITION = 1;
private static final int EVENT_SERVICE_DIED = 2;
- private static final int EVENT_SERVICE_STATE_CHANGE = 3;
+ private static final int EVENT_RESOURCES_AVAILABLE = 3;
+ private static final int EVENT_MODEL_UNLOADED = 4;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mId;
private EventHandlerDelegate mEventHandlerDelegate;
@@ -120,6 +121,7 @@ public class SoundTriggerModule {
* @param soundModelHandle an array of int where the sound model handle will be returned.
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
* system permission
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -181,6 +183,7 @@ public class SoundTriggerModule {
* recognition mode, keyphrases, users, minimum confidence levels...
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
* system permission
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -333,8 +336,11 @@ public class SoundTriggerModule {
listener.onRecognition(
(SoundTrigger.RecognitionEvent) msg.obj);
break;
- case EVENT_SERVICE_STATE_CHANGE:
- listener.onServiceStateChange((int) msg.obj);
+ case EVENT_RESOURCES_AVAILABLE:
+ listener.onResourcesAvailable();
+ break;
+ case EVENT_MODEL_UNLOADED:
+ listener.onModelUnloaded((Integer) msg.obj);
break;
case EVENT_SERVICE_DIED:
listener.onServiceDied();
@@ -348,27 +354,32 @@ public class SoundTriggerModule {
}
@Override
- public synchronized void onRecognition(int handle, RecognitionEvent event)
+ public synchronized void onRecognition(int handle, RecognitionEvent event,
+ int captureSession)
throws RemoteException {
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
- ConversionUtil.aidl2apiRecognitionEvent(handle, event));
+ ConversionUtil.aidl2apiRecognitionEvent(handle, captureSession, event));
mHandler.sendMessage(m);
}
@Override
- public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event)
+ public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
- ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event));
+ ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, captureSession, event));
mHandler.sendMessage(m);
}
@Override
- public synchronized void onRecognitionAvailabilityChange(boolean available)
- throws RemoteException {
- Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE,
- available ? SoundTrigger.SERVICE_STATE_ENABLED
- : SoundTrigger.SERVICE_STATE_DISABLED);
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ Message m = mHandler.obtainMessage(EVENT_MODEL_UNLOADED, modelHandle);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ public synchronized void onResourcesAvailable() throws RemoteException {
+ Message m = mHandler.obtainMessage(EVENT_RESOURCES_AVAILABLE);
mHandler.sendMessage(m);
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 964d7c1658dc..c29a948c2a26 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -384,8 +384,9 @@ public class UsbManager {
"android.hardware.usb.extra.ACCESSORY_START";
/**
- * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
- * This is obtained with SystemClock.elapsedRealtime()
+
+ * A long extra indicating the timestamp just before
+ * sending {@link #ACTION_USB_ACCESSORY_HANDSHAKE}.
* Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
*
* {@hide}
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3cd13a212a4b..9db41c41c31c 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -18,16 +18,20 @@ package android.inputmethodservice;
import android.annotation.MainThread;
import android.annotation.NonNull;
-import android.app.Service;
+import android.annotation.Nullable;
+import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
+import android.os.RemoteException;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputContentInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import android.window.WindowProviderService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,9 +48,22 @@ import java.io.PrintWriter;
* implement. This base class takes care of reporting your InputMethod from
* the service when clients bind to it, but provides no standard implementation
* of the InputMethod interface itself. Derived classes must implement that
- * interface.
+ * interface.</p>
+ *
+ * <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft
+ * input may not be the entire screen. For example, some devices may support to show the soft input
+ * on only half of screen.</p>
+ *
+ * <p>In that case, moving the soft input from one half screen to another will trigger a
+ * {@link android.content.res.Resources} update to match the new {@link Configuration} and
+ * this {@link AbstractInputMethodService} may also receive a
+ * {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes
+ * </p>
+ *
+ * @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration)
+ * @see Context#isUiContext Context#isUiContext to see the concept of UI Context.
*/
-public abstract class AbstractInputMethodService extends Service
+public abstract class AbstractInputMethodService extends WindowProviderService
implements KeyEvent.Callback {
private InputMethod mInputMethod;
@@ -196,16 +213,6 @@ public abstract class AbstractInputMethodService extends Service
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
/**
- * Dumps the internal state of IME to a protocol buffer output stream.
- *
- * @param proto ProtoOutputStream to dump data to.
- * @param icProto {@link InputConnection} call data in proto format.
- * @hide
- */
- @SuppressWarnings("HiddenAbstractMethod")
- public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto);
-
- /**
* Implement this to handle {@link android.os.Binder#dump Binder.dump()}
* calls on your input method.
*/
@@ -218,9 +225,36 @@ public abstract class AbstractInputMethodService extends Service
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
- return new IInputMethodWrapper(this, mInputMethod);
+ return new IInputMethodWrapper(createInputMethodServiceInternal(), mInputMethod);
}
-
+
+ /**
+ * Used to inject custom {@link InputMethodServiceInternal}.
+ *
+ * @return the {@link InputMethodServiceInternal} to be used.
+ */
+ @NonNull
+ InputMethodServiceInternal createInputMethodServiceInternal() {
+ return new InputMethodServiceInternal() {
+ /**
+ * {@inheritDoc}
+ */
+ @NonNull
+ @Override
+ public Context getContext() {
+ return AbstractInputMethodService.this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ AbstractInputMethodService.this.dump(fd, fout, args);
+ }
+ };
+ }
+
/**
* Implement this to handle trackball events on your input method.
*
@@ -243,38 +277,33 @@ public abstract class AbstractInputMethodService extends Service
return false;
}
- /**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
- *
- * <p>Default implementation does nothing.</p>
- *
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param inputConnection {@link InputConnection} with which
- * {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be
- * called.
- * @return {@code false} if we cannot allow a temporary access permission.
- * @hide
- */
- public void exposeContent(@NonNull InputContentInfo inputContentInfo,
- @NonNull InputConnection inputConnection) {
- return;
+ // TODO(b/149463653): remove it in T. We missed the API deadline in S.
+ /** @hide */
+ @Override
+ public final boolean isUiContext() {
+ return true;
}
- /**
- * Called when the user took some actions that should be taken into consideration to update the
- * MRU list for input method rotation.
- *
- * @hide
- */
- public void notifyUserActionIfNecessary() {
+ /** @hide */
+ @Override
+ public final int getWindowType() {
+ return WindowManager.LayoutParams.TYPE_INPUT_METHOD;
}
/** @hide */
@Override
- public final boolean isUiContext() {
- return true;
+ @Nullable
+ public final Bundle getWindowContextOptions() {
+ return null;
+ }
+
+ /** @hide */
+ @Override
+ public final int getInitialDisplayId() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().getImeDisplayId();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9198eb74d1f8..90990b45e552 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -47,7 +47,6 @@ import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputConnectionWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -77,7 +76,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
- final WeakReference<AbstractInputMethodService> mTarget;
+ final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@UnsupportedAppUsage
final HandlerCaller mCaller;
@@ -86,7 +85,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
/**
* This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()}
- * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been
+ * so that {@link RemoteInputConnection} can query if {@link #unbindInput()} has already been
* called or not, mainly to avoid unnecessary blocking operations.
*
* <p>This field must be set and cleared only from the binder thread(s), where the system
@@ -130,12 +129,12 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
- public IInputMethodWrapper(AbstractInputMethodService context, InputMethod inputMethod) {
- mTarget = new WeakReference<>(context);
- mContext = context.getApplicationContext();
+ IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod) {
+ mTarget = new WeakReference<>(imsInternal);
+ mContext = imsInternal.getContext().getApplicationContext();
mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
mInputMethod = new WeakReference<>(inputMethod);
- mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+ mTargetSdkVersion = imsInternal.getContext().getApplicationInfo().targetSdkVersion;
}
@MainThread
@@ -150,7 +149,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
switch (msg.what) {
case DO_DUMP: {
- AbstractInputMethodService target = mTarget.get();
+ InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
@@ -170,8 +169,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_INITIALIZE_INTERNAL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
- (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
+ inputMethod.initializeInternal((IBinder) args.arg1,
+ (IInputMethodPrivilegedOperations) args.arg2, msg.arg1);
} finally {
args.recycle();
}
@@ -192,7 +191,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
SomeArgs moreArgs = (SomeArgs) args.arg5;
final InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(
+ ? new RemoteInputConnection(
mTarget, inputContext, moreArgs.argi3, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
@@ -252,11 +251,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
- AbstractInputMethodService target = mTarget.get();
+ InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
- if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
fout.println("Permission Denial: can't dump InputMethodManager from from pid="
@@ -279,11 +278,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void initializeInternal(IBinder token, int displayId,
- IInputMethodPrivilegedOperations privOps, int configChanges) {
+ public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
+ int configChanges) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
- configChanges));
+ mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, configChanges, token, privOps));
}
@BinderThread
@@ -303,7 +301,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCancellationGroup = new CancellationGroup();
// This IInputContext is guaranteed to implement all the methods.
final int missingMethodFlags = 0;
- InputConnection ic = new InputConnectionWrapper(mTarget,
+ InputConnection ic = new RemoteInputConnection(mTarget,
IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
mCancellationGroup);
InputBinding nu = new InputBinding(ic, binding);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 881e0cfb58d7..dadea6792ac1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -55,7 +55,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
@@ -94,7 +93,6 @@ import android.text.method.MovementMethod;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.KeyCharacterMap;
@@ -133,6 +131,7 @@ import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -589,7 +588,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
- public final void initializeInternal(@NonNull IBinder token, int displayId,
+ public final void initializeInternal(@NonNull IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
Log.w(TAG, "The token has already registered, ignore this initialization.");
@@ -599,7 +598,6 @@ public class InputMethodService extends AbstractInputMethodService {
mConfigTracker.onInitialize(configChanges);
mPrivOps.set(privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
- updateInputMethodDisplay(displayId);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -629,29 +627,13 @@ public class InputMethodService extends AbstractInputMethodService {
throw new IllegalStateException(
"attachToken() must be called at most once. token=" + token);
}
+ attachToWindowToken(token);
mToken = token;
mWindow.setToken(token);
}
/**
* {@inheritDoc}
- * @hide
- */
- @MainThread
- @Override
- public void updateInputMethodDisplay(int displayId) {
- if (getDisplayId() == displayId) {
- return;
- }
- // Update display for adding IME window to the right display.
- // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
- // for update resources & configuration correctly when show soft input
- // in non-default display.
- updateDisplay(displayId);
- }
-
- /**
- * {@inheritDoc}
*
* <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
*/
@@ -757,7 +739,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
ImeTracing.getInstance().triggerServiceDump(
- "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this,
+ "InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
@@ -814,7 +796,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
ImeTracing.getInstance().triggerServiceDump(
- "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this,
+ "InputMethodService.InputMethodImpl#showSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
@@ -2239,7 +2221,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
null /* icProto */);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
@@ -2318,7 +2300,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
ImeTracing.getInstance().triggerServiceDump(
- "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this,
+ "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
if (setVisible) {
cancelImeSurfaceRemoval();
@@ -2347,7 +2329,7 @@ public class InputMethodService extends AbstractInputMethodService {
public void hideWindow() {
if (DEBUG) Log.v(TAG, "CALL: hideWindow");
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
mWindowVisible = false;
finishViews(false /* finishingInput */);
@@ -2420,7 +2402,7 @@ public class InputMethodService extends AbstractInputMethodService {
void doFinishInput() {
if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", mDumper,
null /* icProto */);
finishViews(true /* finishingInput */);
if (mInputStarted) {
@@ -2437,7 +2419,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (!restarting && mInputStarted) {
doFinishInput();
}
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", mDumper,
null /* icProto */);
mInputStarted = true;
mStartedInputConnection = ic;
@@ -2597,7 +2579,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
null /* icProto */);
mPrivOps.hideMySoftInput(flags);
}
@@ -2611,7 +2593,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param flags Provides additional operating flags.
*/
public final void requestShowSelf(int flags) {
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
null /* icProto */);
mPrivOps.showMySoftInput(flags);
}
@@ -3298,67 +3280,91 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
+ * Used to inject custom {@link InputMethodServiceInternal}.
*
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param inputConnection {@link InputConnection} with which
- * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
- * @hide
+ * @return the {@link InputMethodServiceInternal} to be used.
*/
+ @NonNull
@Override
- public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
- @NonNull InputConnection inputConnection) {
- if (inputConnection == null) {
- return;
- }
- if (getCurrentInputConnection() != inputConnection) {
- return;
- }
- exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
- }
+ final InputMethodServiceInternal createInputMethodServiceInternal() {
+ return new InputMethodServiceInternal() {
+ /**
+ * {@inheritDoc}
+ */
+ @NonNull
+ @Override
+ public Context getContext() {
+ return InputMethodService.this;
+ }
- /**
- * {@inheritDoc}
- * @hide
- */
- @AnyThread
- @Override
- public final void notifyUserActionIfNecessary() {
- synchronized (mLock) {
- if (mNotifyUserActionSent) {
- return;
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void exposeContent(@NonNull InputContentInfo inputContentInfo,
+ @NonNull InputConnection inputConnection) {
+ if (inputConnection == null) {
+ return;
+ }
+ if (getCurrentInputConnection() != inputConnection) {
+ return;
+ }
+ exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
}
- mPrivOps.notifyUserActionAsync();
- mNotifyUserActionSent = true;
- }
- }
- /**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
- *
- * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
- * InputConnection)} for details.</p>
- *
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param editorInfo The editor that receives {@link InputContentInfo}.
- */
- private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
- @NonNull EditorInfo editorInfo) {
- final Uri contentUri = inputContentInfo.getContentUri();
- final IInputContentUriToken uriToken =
- mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
- if (uriToken == null) {
- Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
- + " packageName=" + editorInfo.packageName);
- return;
- }
- inputContentInfo.setUriToken(uriToken);
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyUserActionIfNecessary() {
+ synchronized (mLock) {
+ if (mNotifyUserActionSent) {
+ return;
+ }
+ mPrivOps.notifyUserActionAsync();
+ mNotifyUserActionSent = true;
+ }
+ }
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * <p>See {@link #exposeContent(InputContentInfo, InputConnection)} for details.</p>
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to
+ * the application. This cannot be {@code null}.
+ * @param editorInfo The editor that receives {@link InputContentInfo}.
+ */
+ private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
+ @NonNull EditorInfo editorInfo) {
+ final Uri contentUri = inputContentInfo.getContentUri();
+ final IInputContentUriToken uriToken =
+ mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
+ if (uriToken == null) {
+ Log.e(TAG, "createInputContentAccessToken failed. contentUri="
+ + contentUri.toString() + " packageName=" + editorInfo.packageName);
+ return;
+ }
+ inputContentInfo.setUriToken(uriToken);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter fout, String[]args) {
+ InputMethodService.this.dump(fd, fout, args);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void triggerServiceDump(String where, @Nullable byte[] icProto) {
+ ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
+ }
+ };
}
private int mapToImeWindowStatus() {
@@ -3428,42 +3434,44 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mSettingsObserver=" + mSettingsObserver);
}
- /**
- * @hide
- */
- @Override
- public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) {
- final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
- mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
- proto.write(VIEWS_CREATED, mViewsCreated);
- proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
- proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
- proto.write(WINDOW_VISIBLE, mWindowVisible);
- proto.write(IN_SHOW_WINDOW, mInShowWindow);
- proto.write(CONFIGURATION, getResources().getConfiguration().toString());
- proto.write(TOKEN, Objects.toString(mToken));
- proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
- proto.write(INPUT_STARTED, mInputStarted);
- proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
- proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
- if (mInputEditorInfo != null) {
- mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
- }
- proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
- proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
- proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
- proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
- proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
- proto.write(IS_FULLSCREEN, mIsFullscreen);
- proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
- proto.write(EXTRACTED_TOKEN, mExtractedToken);
- proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
- proto.write(STATUS_ICON, mStatusIcon);
- mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS);
- proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver));
- if (icProto != null) {
- proto.write(INPUT_CONNECTION_CALL, icProto.getBytes());
- }
- proto.end(token);
- }
+ private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto) {
+ final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
+ mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
+ proto.write(VIEWS_CREATED, mViewsCreated);
+ proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
+ proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
+ proto.write(WINDOW_VISIBLE, mWindowVisible);
+ proto.write(IN_SHOW_WINDOW, mInShowWindow);
+ proto.write(CONFIGURATION, getResources().getConfiguration().toString());
+ proto.write(TOKEN, Objects.toString(mToken));
+ proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
+ proto.write(INPUT_STARTED, mInputStarted);
+ proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
+ proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
+ if (mInputEditorInfo != null) {
+ mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
+ }
+ proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
+ proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
+ proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
+ proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
+ proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
+ proto.write(IS_FULLSCREEN, mIsFullscreen);
+ proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
+ proto.write(EXTRACTED_TOKEN, mExtractedToken);
+ proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
+ proto.write(STATUS_ICON, mStatusIcon);
+ mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS);
+ proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver));
+ if (icProto != null) {
+ proto.write(INPUT_CONNECTION_CALL, icProto);
+ }
+ proto.end(token);
+ }
+ };
}
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
new file mode 100644
index 000000000000..7cd4ff61b2c8
--- /dev/null
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -0,0 +1,86 @@
+/*
+ * 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.inputmethodservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A set of internal methods exposed by {@link InputMethodService} to be called only from other
+ * framework classes for internal use.
+ *
+ * <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
+ */
+interface InputMethodServiceInternal {
+ /**
+ * @return {@link Context} associated with the service.
+ */
+ @NonNull
+ Context getContext();
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application. This cannot be {@code null}.
+ * @param inputConnection {@link InputConnection} with which
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}
+ * will be called.
+ */
+ default void exposeContent(@NonNull InputContentInfo inputContentInfo,
+ @NonNull InputConnection inputConnection) {
+ }
+
+ /**
+ * Called when the user took some actions that should be taken into consideration to update the
+ * MRU list for input method rotation.
+ */
+ default void notifyUserActionIfNecessary() {
+ }
+
+ /**
+ * Called when the system is asking the IME to dump its information for debugging.
+ *
+ * <p>The caller is responsible for checking {@link android.Manifest.permission.DUMP}.</p>
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param fout The file to which you should dump your state. This will be
+ * closed for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ default void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ }
+
+ /**
+ * Called with {@link com.android.internal.inputmethod.ImeTracing#triggerServiceDump(String,
+ * com.android.internal.inputmethod.ImeTracing.ServiceDumper, byte[])} needs to be triggered
+ * with the given parameters.
+ *
+ * @param where {@code where} parameter to be passed.
+ * @param icProto {@code icProto} parameter to be passed.
+ */
+ default void triggerServiceDump(String where, @Nullable byte[] icProto) {
+ }
+}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
deleted file mode 100644
index f352f05d0488..000000000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.inputmethodservice;
-
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.CancellationGroup;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InputConnectionWrapper;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Re-dispatches all the incoming per-client events to the specified {@link Looper} thread.
- *
- * <p>There are three types of per-client callbacks.</p>
- *
- * <ul>
- * <li>{@link IInputMethodSession} - from the IME client</li>
- * <li>{@link IMultiClientInputMethodSession} - from MultiClientInputMethodManagerService</li>
- * <li>{@link InputChannel} - from the IME client</li>
- * </ul>
- *
- * <p>This class serializes all the incoming events among those channels onto
- * {@link MultiClientInputMethodServiceDelegate.ClientCallback} on the specified {@link Looper}
- * thread.</p>
- */
-final class MultiClientInputMethodClientCallbackAdaptor {
- static final boolean DEBUG = false;
- static final String TAG = MultiClientInputMethodClientCallbackAdaptor.class.getSimpleName();
-
- private final Object mSessionLock = new Object();
- @GuardedBy("mSessionLock")
- CallbackImpl mCallbackImpl;
- @GuardedBy("mSessionLock")
- InputChannel mReadChannel;
- @GuardedBy("mSessionLock")
- KeyEvent.DispatcherState mDispatcherState;
- @GuardedBy("mSessionLock")
- Handler mHandler;
- @GuardedBy("mSessionLock")
- @Nullable
- InputEventReceiver mInputEventReceiver;
-
- private final CancellationGroup mCancellationGroup = new CancellationGroup();
-
- IInputMethodSession.Stub createIInputMethodSession() {
- synchronized (mSessionLock) {
- return new InputMethodSessionImpl(
- mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
- }
- }
-
- IMultiClientInputMethodSession.Stub createIMultiClientInputMethodSession() {
- synchronized (mSessionLock) {
- return new MultiClientInputMethodSessionImpl(
- mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
- }
- }
-
- MultiClientInputMethodClientCallbackAdaptor(
- MultiClientInputMethodServiceDelegate.ClientCallback clientCallback, Looper looper,
- KeyEvent.DispatcherState dispatcherState, InputChannel readChannel) {
- synchronized (mSessionLock) {
- mCallbackImpl = new CallbackImpl(this, clientCallback);
- mDispatcherState = dispatcherState;
- mHandler = new Handler(looper, null, true);
- mReadChannel = readChannel;
- mInputEventReceiver = new ImeInputEventReceiver(mReadChannel, mHandler.getLooper(),
- mCancellationGroup, mDispatcherState, mCallbackImpl.mOriginalCallback);
- }
- }
-
- private static final class KeyEventCallbackAdaptor implements KeyEvent.Callback {
- private final MultiClientInputMethodServiceDelegate.ClientCallback mLocalCallback;
-
- KeyEventCallbackAdaptor(
- MultiClientInputMethodServiceDelegate.ClientCallback callback) {
- mLocalCallback = callback;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- return mLocalCallback.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- return mLocalCallback.onKeyLongPress(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- return mLocalCallback.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- return mLocalCallback.onKeyMultiple(keyCode, event);
- }
- }
-
- private static final class ImeInputEventReceiver extends InputEventReceiver {
- private final CancellationGroup mCancellationGroupOnFinishSession;
- private final KeyEvent.DispatcherState mDispatcherState;
- private final MultiClientInputMethodServiceDelegate.ClientCallback mClientCallback;
- private final KeyEventCallbackAdaptor mKeyEventCallbackAdaptor;
-
- ImeInputEventReceiver(InputChannel readChannel, Looper looper,
- CancellationGroup cancellationGroupOnFinishSession,
- KeyEvent.DispatcherState dispatcherState,
- MultiClientInputMethodServiceDelegate.ClientCallback callback) {
- super(readChannel, looper);
- mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
- mDispatcherState = dispatcherState;
- mClientCallback = callback;
- mKeyEventCallbackAdaptor = new KeyEventCallbackAdaptor(callback);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- if (mCancellationGroupOnFinishSession.isCanceled()) {
- // The session has been finished.
- finishInputEvent(event, false);
- return;
- }
- boolean handled = false;
- try {
- if (event instanceof KeyEvent) {
- final KeyEvent keyEvent = (KeyEvent) event;
- handled = keyEvent.dispatch(mKeyEventCallbackAdaptor, mDispatcherState,
- mKeyEventCallbackAdaptor);
- } else {
- final MotionEvent motionEvent = (MotionEvent) event;
- if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
- handled = mClientCallback.onTrackballEvent(motionEvent);
- } else {
- handled = mClientCallback.onGenericMotionEvent(motionEvent);
- }
- }
- } finally {
- finishInputEvent(event, handled);
- }
- }
- }
-
- private static final class InputMethodSessionImpl extends IInputMethodSession.Stub {
- private final Object mSessionLock;
- @GuardedBy("mSessionLock")
- private CallbackImpl mCallbackImpl;
- @GuardedBy("mSessionLock")
- private Handler mHandler;
- private final CancellationGroup mCancellationGroupOnFinishSession;
-
- InputMethodSessionImpl(Object lock, CallbackImpl callback, Handler handler,
- CancellationGroup cancellationGroupOnFinishSession) {
- mSessionLock = lock;
- mCallbackImpl = callback;
- mHandler = handler;
- mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
- }
-
- @Override
- public void updateExtractedText(int token, ExtractedText text) {
- reportNotSupported();
- }
-
- @Override
- public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd,
- int candidatesStart, int candidatesEnd) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- final SomeArgs args = SomeArgs.obtain();
- args.argi1 = oldSelStart;
- args.argi2 = oldSelEnd;
- args.argi3 = newSelStart;
- args.argi4 = newSelEnd;
- args.argi5 = candidatesStart;
- args.argi6 = candidatesEnd;
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::updateSelection, mCallbackImpl, args));
- }
- }
-
- @Override
- public void viewClicked(boolean focusChanged) {
- reportNotSupported();
- }
-
- @Override
- public void updateCursor(Rect newCursor) {
- reportNotSupported();
- }
-
- @Override
- public void displayCompletions(CompletionInfo[] completions) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::displayCompletions, mCallbackImpl, completions));
- }
- }
-
- @Override
- public void appPrivateCommand(String action, Bundle data) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::appPrivateCommand, mCallbackImpl, action, data));
- }
- }
-
- @Override
- public void finishSession() {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mCancellationGroupOnFinishSession.cancelAll();
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::finishSession, mCallbackImpl));
- mCallbackImpl = null;
- mHandler = null;
- }
- }
-
- @Override
- public void updateCursorAnchorInfo(CursorAnchorInfo info) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::updateCursorAnchorInfo, mCallbackImpl, info));
- }
- }
-
- @Override
- public final void notifyImeHidden() {
- // no-op for multi-session since IME is responsible controlling navigation bar buttons.
- reportNotSupported();
- }
-
- @Override
- public void removeImeSurface() {
- // no-op for multi-session
- reportNotSupported();
- }
-
- @Override
- public void finishInput() throws RemoteException {
- // no-op for multi-session
- reportNotSupported();
- }
- }
-
- private static final class MultiClientInputMethodSessionImpl
- extends IMultiClientInputMethodSession.Stub {
- private final Object mSessionLock;
- @GuardedBy("mSessionLock")
- private CallbackImpl mCallbackImpl;
- @GuardedBy("mSessionLock")
- private Handler mHandler;
- private final CancellationGroup mCancellationGroupOnFinishSession;
-
- MultiClientInputMethodSessionImpl(Object lock, CallbackImpl callback,
- Handler handler, CancellationGroup cancellationGroupOnFinishSession) {
- mSessionLock = lock;
- mCallbackImpl = callback;
- mHandler = handler;
- mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
- }
-
- @Override
- public void startInputOrWindowGainedFocus(@Nullable IInputContext inputContext,
- int missingMethods, @Nullable EditorInfo editorInfo, int controlFlags,
- @SoftInputModeFlags int softInputMode, int windowHandle) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- final SomeArgs args = SomeArgs.obtain();
- // TODO(Bug 119211536): Remove dependency on AbstractInputMethodService from ICW
- final WeakReference<AbstractInputMethodService> fakeIMS =
- new WeakReference<>(null);
- args.arg1 = (inputContext == null) ? null
- : new InputConnectionWrapper(fakeIMS, inputContext, missingMethods,
- mCancellationGroupOnFinishSession);
- args.arg2 = editorInfo;
- args.argi1 = controlFlags;
- args.argi2 = softInputMode;
- args.argi3 = windowHandle;
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::startInputOrWindowGainedFocus, mCallbackImpl, args));
- }
- }
-
- @Override
- public void showSoftInput(int flags, ResultReceiver resultReceiver) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::showSoftInput, mCallbackImpl, flags,
- resultReceiver));
- }
- }
-
- @Override
- public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::hideSoftInput, mCallbackImpl, flags,
- resultReceiver));
- }
- }
- }
-
- /**
- * The maim part of adaptor to {@link MultiClientInputMethodServiceDelegate.ClientCallback}.
- */
- @WorkerThread
- private static final class CallbackImpl {
- private final MultiClientInputMethodClientCallbackAdaptor mCallbackAdaptor;
- private final MultiClientInputMethodServiceDelegate.ClientCallback mOriginalCallback;
- private boolean mFinished = false;
-
- CallbackImpl(MultiClientInputMethodClientCallbackAdaptor callbackAdaptor,
- MultiClientInputMethodServiceDelegate.ClientCallback callback) {
- mCallbackAdaptor = callbackAdaptor;
- mOriginalCallback = callback;
- }
-
- void updateSelection(SomeArgs args) {
- try {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onUpdateSelection(args.argi1, args.argi2, args.argi3,
- args.argi4, args.argi5, args.argi6);
- } finally {
- args.recycle();
- }
- }
-
- void displayCompletions(CompletionInfo[] completions) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onDisplayCompletions(completions);
- }
-
- void appPrivateCommand(String action, Bundle data) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onAppPrivateCommand(action, data);
- }
-
- void finishSession() {
- if (mFinished) {
- return;
- }
- mFinished = true;
- mOriginalCallback.onFinishSession();
- synchronized (mCallbackAdaptor.mSessionLock) {
- mCallbackAdaptor.mDispatcherState = null;
- if (mCallbackAdaptor.mReadChannel != null) {
- mCallbackAdaptor.mReadChannel.dispose();
- mCallbackAdaptor.mReadChannel = null;
- }
- mCallbackAdaptor.mInputEventReceiver = null;
- }
- }
-
- void updateCursorAnchorInfo(CursorAnchorInfo info) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onUpdateCursorAnchorInfo(info);
- }
-
- void startInputOrWindowGainedFocus(SomeArgs args) {
- try {
- if (mFinished) {
- return;
- }
- final InputConnectionWrapper inputConnection = (InputConnectionWrapper) args.arg1;
- final EditorInfo editorInfo = (EditorInfo) args.arg2;
- final int startInputFlags = args.argi1;
- final int softInputMode = args.argi2;
- final int windowHandle = args.argi3;
- mOriginalCallback.onStartInputOrWindowGainedFocus(inputConnection, editorInfo,
- startInputFlags, softInputMode, windowHandle);
- } finally {
- args.recycle();
- }
- }
-
- void showSoftInput(int flags, ResultReceiver resultReceiver) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onShowSoftInput(flags, resultReceiver);
- }
-
- void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onHideSoftInput(flags, resultReceiver);
- }
- }
-
- private static void reportNotSupported() {
- if (DEBUG) {
- Log.d(TAG, Debug.getCaller() + " is not supported");
- }
- }
-}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
deleted file mode 100644
index 0a2316508f09..000000000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.inputmethodservice;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-import com.android.internal.inputmethod.StartInputFlags;
-
-/**
- * Defines all the public APIs and interfaces that are necessary to implement multi-client IMEs.
- *
- * <p>Actual implementation is further delegated to
- * {@link MultiClientInputMethodServiceDelegateImpl}.</p>
- *
- * @hide
- */
-public final class MultiClientInputMethodServiceDelegate {
- // @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.inputmethodservice.MultiClientInputMethodService";
-
- /**
- * Special value that is guaranteed to be not used for IME client ID.
- */
- public static final int INVALID_CLIENT_ID = -1;
-
- /**
- * Special value that is guaranteed to be not used for window handle.
- */
- public static final int INVALID_WINDOW_HANDLE = -1;
-
- private final MultiClientInputMethodServiceDelegateImpl mImpl;
-
- /**
- * Top-level callbacks for this {@link MultiClientInputMethodServiceDelegate}.
- */
- public interface ServiceCallback {
- /**
- * Called when this {@link MultiClientInputMethodServiceDelegate} is recognized by the
- * system and privileged operations like {@link #createInputMethodWindowToken(int)} are
- * ready to be called.
- */
- void initialized();
-
- /**
- * Called when a new IME client is recognized by the system.
- *
- * <p>Once the IME receives this callback, the IME can start interacting with the IME client
- * by calling {@link #acceptClient(int, ClientCallback, KeyEvent.DispatcherState, Looper)}.
- * </p>
- *
- * @param clientId ID of the client.
- * @param uid UID of the IME client.
- * @param pid PID of the IME client.
- * @param selfReportedDisplayId display ID reported from the IME client. Since the system
- * does not validate this display ID, and at any time the IME client can lose the
- * access to this display ID, the IME needs to call
- * {@link #isUidAllowedOnDisplay(int, int)} to check whether the IME client still
- * has access to this display or not.
- */
- void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
-
- /**
- * Called when an IME client is being destroyed.
- *
- * @param clientId ID of the client.
- */
- void removeClient(int clientId);
- }
-
- /**
- * Per-client callbacks.
- */
- public interface ClientCallback {
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#sendAppPrivateCommand(View, String, Bundle)}.
- *
- * @param action Name of the command to be performed.
- * @param data Any data to include with the command.
- * @see android.inputmethodservice.InputMethodService#onAppPrivateCommand(String, Bundle)
- */
- void onAppPrivateCommand(String action, Bundle data);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#displayCompletions(View, CompletionInfo[])}.
- *
- * @param completions Completion information provided from the IME client.
- * @see android.inputmethodservice.InputMethodService#onDisplayCompletions(CompletionInfo[])
- */
- void onDisplayCompletions(CompletionInfo[] completions);
-
- /**
- * Called when this callback session is closed. No further callback should not happen on
- * this callback object.
- */
- void onFinishSession();
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int)} or
- * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int,
- * ResultReceiver)}.
- *
- * @param flags The flag passed by the client.
- * @param resultReceiver The {@link ResultReceiver} passed by the client.
- * @see android.inputmethodservice.InputMethodService#onWindowHidden()
- */
- void onHideSoftInput(int flags, ResultReceiver resultReceiver);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or {@link
- * android.view.inputmethod.InputMethodManager#showSoftInput(View, int, ResultReceiver)}.
- *
- * @param flags The flag passed by the client.
- * @param resultReceiver The {@link ResultReceiver} passed by the client.
- * @see android.inputmethodservice.InputMethodService#onWindowShown()
- */
- void onShowSoftInput(int flags, ResultReceiver resultReceiver);
-
- /**
- * A generic callback when {@link InputConnection} is being established.
- *
- * @param inputConnection The {@link InputConnection} to be established.
- * @param editorInfo The {@link EditorInfo} reported from the IME client.
- * @param startInputFlags Any combinations of {@link StartInputFlags}.
- * @param softInputMode SoftWindowMode specified to this window.
- * @param targetWindowHandle A unique Window token.
- * @see android.inputmethodservice.InputMethodService#onStartInput(EditorInfo, boolean)
- */
- void onStartInputOrWindowGainedFocus(
- @Nullable InputConnection inputConnection,
- @Nullable EditorInfo editorInfo,
- @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode,
- int targetWindowHandle);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#updateCursorAnchorInfo(View,
- * CursorAnchorInfo)}.
- *
- * @param info The {@link CursorAnchorInfo} passed by the client.
- * @see android.inputmethodservice.InputMethodService#onUpdateCursorAnchorInfo(
- * CursorAnchorInfo)
- */
- void onUpdateCursorAnchorInfo(CursorAnchorInfo info);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#updateSelection(View, int, int, int, int)}.
- *
- * @param oldSelStart The previous selection start index.
- * @param oldSelEnd The previous selection end index.
- * @param newSelStart The new selection start index.
- * @param newSelEnd The new selection end index.
- * @param candidatesStart The new candidate start index.
- * @param candidatesEnd The new candidate end index.
- * @see android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
- * int, int)
- */
- void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
- int candidatesStart, int candidatesEnd);
-
- /**
- * Called to give a chance for the IME to intercept generic motion events before they are
- * processed by the application.
- *
- * @param event {@link MotionEvent} that is about to be handled by the IME client.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onGenericMotionEvent(MotionEvent)
- */
- boolean onGenericMotionEvent(MotionEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key down events before they are
- * processed by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key down event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyDown(int, KeyEvent)
- */
- boolean onKeyDown(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key long press events before they are
- * processed by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key long press event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyLongPress(int, KeyEvent)
- */
- boolean onKeyLongPress(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key multiple events before they are
- * processed by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key multiple event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyMultiple(int, int, KeyEvent)
- */
- boolean onKeyMultiple(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key up events before they are processed
- * by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key up event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent)
- */
- boolean onKeyUp(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept generic motion events before they are
- * processed by the application.
- *
- * @param event {@link MotionEvent} that is about to be handled by the IME client.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onTrackballEvent(MotionEvent)
- */
- boolean onTrackballEvent(MotionEvent event);
- }
-
- private MultiClientInputMethodServiceDelegate(Context context,
- ServiceCallback serviceCallback) {
- mImpl = new MultiClientInputMethodServiceDelegateImpl(context, serviceCallback);
- }
-
- /**
- * Must be called by the multi-client IME implementer to create
- * {@link MultiClientInputMethodServiceDelegate}.
- *
- * @param context {@link Context} with which the delegate should interact with the system.
- * @param serviceCallback {@link ServiceCallback} to receive service-level callbacks.
- * @return A new instance of {@link MultiClientInputMethodServiceDelegate}.
- */
- public static MultiClientInputMethodServiceDelegate create(Context context,
- ServiceCallback serviceCallback) {
- return new MultiClientInputMethodServiceDelegate(context, serviceCallback);
- }
-
- /**
- * Must be called by the multi-client IME service when {@link android.app.Service#onDestroy()}
- * is called.
- */
- public void onDestroy() {
- mImpl.onDestroy();
- }
-
- /**
- * Must be called by the multi-client IME service when
- * {@link android.app.Service#onBind(Intent)} is called.
- *
- * @param intent {@link Intent} passed to {@link android.app.Service#onBind(Intent)}.
- * @return An {@link IBinder} object that needs to be returned from
- * {@link android.app.Service#onBind(Intent)}.
- */
- public IBinder onBind(Intent intent) {
- return mImpl.onBind(intent);
- }
-
- /**
- * Must be called by the multi-client IME service when
- * {@link android.app.Service#onUnbind(Intent)} is called.
- *
- * @param intent {@link Intent} passed to {@link android.app.Service#onUnbind(Intent)}.
- * @return A boolean value that needs to be returned from
- * {@link android.app.Service#onUnbind(Intent)}.
- */
- public boolean onUnbind(Intent intent) {
- return mImpl.onUnbind(intent);
- }
-
- /**
- * Must be called by the multi-client IME service to create a special window token for IME
- * window.
- *
- * <p>This method is available only after {@link ServiceCallback#initialized()}.</p>
- *
- * @param displayId display ID on which the IME window will be shown.
- * @return Window token to be specified to the IME window/
- */
- public IBinder createInputMethodWindowToken(int displayId) {
- return mImpl.createInputMethodWindowToken(displayId);
- }
-
- /**
- * Must be called by the multi-client IME service to notify the system when the IME is ready to
- * accept callback events from the specified IME client.
- *
- * @param clientId The IME client ID specified in
- * {@link ServiceCallback#addClient(int, int, int, int)}.
- * @param clientCallback The {@link ClientCallback} to receive callback events from this IME
- * client.
- * @param dispatcherState {@link KeyEvent.DispatcherState} to be used when receiving key-related
- * callbacks in {@link ClientCallback}.
- * @param looper {@link Looper} on which {@link ClientCallback} will be called back.
- */
- public void acceptClient(int clientId, ClientCallback clientCallback,
- KeyEvent.DispatcherState dispatcherState, Looper looper) {
- mImpl.acceptClient(clientId, clientCallback, dispatcherState, looper);
- }
-
- /**
- * Must be called by the multi-client IME service to notify the system when the IME is ready to
- * interact with the window in the IME client.
- *
- * @param clientId The IME client ID specified in
- * {@link ServiceCallback#addClient(int, int, int, int)}.
- * @param targetWindowHandle The window handle specified in
- * {@link ClientCallback#onStartInputOrWindowGainedFocus}.
- * @param imeWindowToken The IME window token returned from
- * {@link #createInputMethodWindowToken(int)}.
- */
- public void reportImeWindowTarget(int clientId, int targetWindowHandle,
- IBinder imeWindowToken) {
- mImpl.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
- }
-
- /**
- * Can be called by the multi-client IME service to check if the given {@code uid} is allowed
- * to access to {@code displayId}.
- *
- * @param displayId Display ID to be queried.
- * @param uid UID to be queried.
- * @return {@code true} if {@code uid} is allowed to access to {@code displayId}.
- */
- public boolean isUidAllowedOnDisplay(int displayId, int uid) {
- return mImpl.isUidAllowedOnDisplay(displayId, uid);
- }
-
- /**
- * Can be called by MSIME to activate/deactivate a client when it is gaining/losing focus
- * respectively.
- *
- * @param clientId client ID to activate/deactivate.
- * @param active {@code true} to activate a client.
- */
- public void setActive(int clientId, boolean active) {
- mImpl.setActive(clientId, active);
- }
-}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
deleted file mode 100644
index 04db8d625806..000000000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.inputmethodservice;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.KeyEvent;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.IMultiClientInputMethod;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.MultiClientInputMethodPrivilegedOperations;
-
-import java.lang.annotation.Retention;
-import java.lang.ref.WeakReference;
-
-final class MultiClientInputMethodServiceDelegateImpl {
- private static final String TAG = "MultiClientInputMethodServiceDelegateImpl";
-
- private final Object mLock = new Object();
-
- @Retention(SOURCE)
- @IntDef({InitializationPhase.INSTANTIATED,
- InitializationPhase.ON_BIND_CALLED,
- InitializationPhase.INITIALIZE_CALLED,
- InitializationPhase.ON_UNBIND_CALLED,
- InitializationPhase.ON_DESTROY_CALLED})
- private @interface InitializationPhase {
- int INSTANTIATED = 1;
- int ON_BIND_CALLED = 2;
- int INITIALIZE_CALLED = 3;
- int ON_UNBIND_CALLED = 4;
- int ON_DESTROY_CALLED = 5;
- }
-
- @GuardedBy("mLock")
- @InitializationPhase
- private int mInitializationPhase;
-
- private final MultiClientInputMethodPrivilegedOperations mPrivOps =
- new MultiClientInputMethodPrivilegedOperations();
-
- private final MultiClientInputMethodServiceDelegate.ServiceCallback mServiceCallback;
-
- private final Context mContext;
-
- MultiClientInputMethodServiceDelegateImpl(Context context,
- MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback) {
- mInitializationPhase = InitializationPhase.INSTANTIATED;
- mContext = context;
- mServiceCallback = serviceCallback;
- }
-
- void onDestroy() {
- synchronized (mLock) {
- switch (mInitializationPhase) {
- case InitializationPhase.INSTANTIATED:
- case InitializationPhase.ON_UNBIND_CALLED:
- mInitializationPhase = InitializationPhase.ON_DESTROY_CALLED;
- break;
- default:
- Log.e(TAG, "unexpected state=" + mInitializationPhase);
- break;
- }
- }
- }
-
- private static final class ServiceImpl extends IMultiClientInputMethod.Stub {
- private final WeakReference<MultiClientInputMethodServiceDelegateImpl> mImpl;
-
- ServiceImpl(MultiClientInputMethodServiceDelegateImpl service) {
- mImpl = new WeakReference<>(service);
- }
-
- @Override
- public void initialize(IMultiClientInputMethodPrivilegedOperations privOps) {
- final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
- if (service == null) {
- return;
- }
- synchronized (service.mLock) {
- switch (service.mInitializationPhase) {
- case InitializationPhase.ON_BIND_CALLED:
- service.mPrivOps.set(privOps);
- service.mInitializationPhase = InitializationPhase.INITIALIZE_CALLED;
- service.mServiceCallback.initialized();
- break;
- default:
- Log.e(TAG, "unexpected state=" + service.mInitializationPhase);
- break;
- }
- }
- }
-
- @Override
- public void addClient(int clientId, int uid, int pid, int selfReportedDisplayId) {
- final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
- if (service == null) {
- return;
- }
- service.mServiceCallback.addClient(clientId, uid, pid, selfReportedDisplayId);
- }
-
- @Override
- public void removeClient(int clientId) {
- final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
- if (service == null) {
- return;
- }
- service.mServiceCallback.removeClient(clientId);
- }
- }
-
- IBinder onBind(Intent intent) {
- synchronized (mLock) {
- switch (mInitializationPhase) {
- case InitializationPhase.INSTANTIATED:
- mInitializationPhase = InitializationPhase.ON_BIND_CALLED;
- return new ServiceImpl(this);
- default:
- Log.e(TAG, "unexpected state=" + mInitializationPhase);
- break;
- }
- }
- return null;
- }
-
- boolean onUnbind(Intent intent) {
- synchronized (mLock) {
- switch (mInitializationPhase) {
- case InitializationPhase.ON_BIND_CALLED:
- case InitializationPhase.INITIALIZE_CALLED:
- mInitializationPhase = InitializationPhase.ON_UNBIND_CALLED;
- mPrivOps.dispose();
- break;
- default:
- Log.e(TAG, "unexpected state=" + mInitializationPhase);
- break;
- }
- }
- return false;
- }
-
- IBinder createInputMethodWindowToken(int displayId) {
- return mPrivOps.createInputMethodWindowToken(displayId);
- }
-
- void acceptClient(int clientId,
- MultiClientInputMethodServiceDelegate.ClientCallback clientCallback,
- KeyEvent.DispatcherState dispatcherState, Looper looper) {
- final InputChannel[] channels = InputChannel.openInputChannelPair("MSIMS-session");
- final InputChannel writeChannel = channels[0];
- final InputChannel readChannel = channels[1];
- try {
- final MultiClientInputMethodClientCallbackAdaptor callbackAdaptor =
- new MultiClientInputMethodClientCallbackAdaptor(clientCallback, looper,
- dispatcherState, readChannel);
- mPrivOps.acceptClient(clientId, callbackAdaptor.createIInputMethodSession(),
- callbackAdaptor.createIMultiClientInputMethodSession(), writeChannel);
- } finally {
- writeChannel.dispose();
- }
- }
-
- void reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken) {
- mPrivOps.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
- }
-
- boolean isUidAllowedOnDisplay(int displayId, int uid) {
- return mPrivOps.isUidAllowedOnDisplay(displayId, uid);
- }
-
- void setActive(int clientId, boolean active) {
- mPrivOps.setActive(clientId, active);
- }
-}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 0e9d13595124..4fa98be75b46 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -14,19 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.view;
+package android.inputmethodservice;
import android.annotation.AnyThread;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.inputmethodservice.AbstractInputMethodService;
import android.os.Bundle;
import android.os.Handler;
-import android.os.RemoteException;
-import android.util.imetracing.ImeTracing;
-import android.util.imetracing.InputConnectionHelper;
-import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -40,17 +35,24 @@ import android.view.inputmethod.SurroundingText;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.Completable;
-import com.android.internal.inputmethod.ResultCallbacks;
+import com.android.internal.inputmethod.IInputContextInvoker;
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputConnectionProtoDumper;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethod;
import java.lang.ref.WeakReference;
-public class InputConnectionWrapper implements InputConnection {
- private static final String TAG = "InputConnectionWrapper";
+final class RemoteInputConnection implements InputConnection {
+ private static final String TAG = "RemoteInputConnection";
private static final int MAX_WAIT_TIME_MILLIS = 2000;
- private final IInputContext mIInputContext;
+
+ @NonNull
+ private final IInputContextInvoker mInvoker;
+
@NonNull
- private final WeakReference<AbstractInputMethodService> mInputMethodService;
+ private final WeakReference<InputMethodServiceInternal> mInputMethodService;
@MissingMethodFlags
private final int mMissingMethods;
@@ -64,12 +66,12 @@ public class InputConnectionWrapper implements InputConnection {
@NonNull
private final CancellationGroup mCancellationGroup;
- public InputConnectionWrapper(
- @NonNull WeakReference<AbstractInputMethodService> inputMethodService,
+ RemoteInputConnection(
+ @NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
IInputContext inputContext, @MissingMethodFlags int missingMethods,
@NonNull CancellationGroup cancellationGroup) {
mInputMethodService = inputMethodService;
- mIInputContext = inputContext;
+ mInvoker = IInputContextInvoker.create(inputContext);
mMissingMethods = missingMethods;
mCancellationGroup = cancellationGroup;
}
@@ -84,21 +86,15 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
- final Completable.CharSequence value = Completable.createCharSequence();
- try {
- mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- CharSequence result = Completable.getResultOrNull(
+ final Completable.CharSequence value = mInvoker.getTextAfterCursor(length, flags);
+ final CharSequence result = Completable.getResultOrNull(
value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetTextAfterCursorProto(length,
+ final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(length,
flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
}
return result;
@@ -114,21 +110,15 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
- final Completable.CharSequence value = Completable.createCharSequence();
- try {
- mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- CharSequence result = Completable.getResultOrNull(
+ final Completable.CharSequence value = mInvoker.getTextBeforeCursor(length, flags);
+ final CharSequence result = Completable.getResultOrNull(
value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(length,
+ final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(length,
flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
}
return result;
@@ -144,21 +134,15 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return null;
}
- final Completable.CharSequence value = Completable.createCharSequence();
- try {
- mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- CharSequence result = Completable.getResultOrNull(
+ final Completable.CharSequence value = mInvoker.getSelectedText(flags);
+ final CharSequence result = Completable.getResultOrNull(
value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetSelectedTextProto(flags,
+ final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getSelectedText", icProto);
}
return result;
@@ -187,22 +171,16 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return null;
}
- final Completable.SurroundingText value = Completable.createSurroundingText();
- try {
- mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
- ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- SurroundingText result = Completable.getResultOrNull(
+ final Completable.SurroundingText value = mInvoker.getSurroundingText(beforeLength,
+ afterLength, flags);
+ final SurroundingText result = Completable.getResultOrNull(
value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetSurroundingTextProto(
+ final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
beforeLength, afterLength, flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getSurroundingText", icProto);
}
return result;
@@ -214,21 +192,15 @@ public class InputConnectionWrapper implements InputConnection {
return 0;
}
- final Completable.Int value = Completable.createInt();
- try {
- mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return 0;
- }
- int result = Completable.getResultOrZero(
+ final Completable.Int value = mInvoker.getCursorCapsMode(reqModes);
+ final int result = Completable.getResultOrZero(
value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetCursorCapsModeProto(
+ final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
reqModes, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
}
return result;
@@ -240,21 +212,15 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
- final Completable.ExtractedText value = Completable.createExtractedText();
- try {
- mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- ExtractedText result = Completable.getResultOrNull(
+ final Completable.ExtractedText value = mInvoker.getExtractedText(request, flags);
+ final ExtractedText result = Completable.getResultOrNull(
value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetExtractedTextProto(
+ final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
request, flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getExtractedText", icProto);
}
return result;
@@ -262,18 +228,16 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public boolean commitText(CharSequence text, int newCursorPosition) {
- try {
- mIInputContext.commitText(text, newCursorPosition);
+ final boolean handled = mInvoker.commitText(text, newCursorPosition);
+ if (handled) {
notifyUserActionIfNecessary();
- return true;
- } catch (RemoteException e) {
- return false;
}
+ return handled;
}
@AnyThread
private void notifyUserActionIfNecessary() {
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService == null) {
// This basically should not happen, because it's the the caller of this method.
return;
@@ -287,52 +251,27 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- try {
- mIInputContext.commitCompletion(text);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.commitCompletion(text);
}
@AnyThread
public boolean commitCorrection(CorrectionInfo correctionInfo) {
- try {
- mIInputContext.commitCorrection(correctionInfo);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.commitCorrection(correctionInfo);
}
@AnyThread
public boolean setSelection(int start, int end) {
- try {
- mIInputContext.setSelection(start, end);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.setSelection(start, end);
}
@AnyThread
public boolean performEditorAction(int actionCode) {
- try {
- mIInputContext.performEditorAction(actionCode);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performEditorAction(actionCode);
}
@AnyThread
public boolean performContextMenuAction(int id) {
- try {
- mIInputContext.performContextMenuAction(id);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performContextMenuAction(id);
}
@AnyThread
@@ -341,84 +280,50 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- try {
- mIInputContext.setComposingRegion(start, end);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.setComposingRegion(start, end);
}
@AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition) {
- try {
- mIInputContext.setComposingText(text, newCursorPosition);
+ final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
+ if (handled) {
notifyUserActionIfNecessary();
- return true;
- } catch (RemoteException e) {
- return false;
}
+ return handled;
}
@AnyThread
public boolean finishComposingText() {
- try {
- mIInputContext.finishComposingText();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.finishComposingText();
}
@AnyThread
public boolean beginBatchEdit() {
- try {
- mIInputContext.beginBatchEdit();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.beginBatchEdit();
}
@AnyThread
public boolean endBatchEdit() {
- try {
- mIInputContext.endBatchEdit();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.endBatchEdit();
}
@AnyThread
public boolean sendKeyEvent(KeyEvent event) {
- try {
- mIInputContext.sendKeyEvent(event);
+ final boolean handled = mInvoker.sendKeyEvent(event);
+ if (handled) {
notifyUserActionIfNecessary();
- return true;
- } catch (RemoteException e) {
- return false;
}
+ return handled;
}
@AnyThread
public boolean clearMetaKeyStates(int states) {
- try {
- mIInputContext.clearMetaKeyStates(states);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.clearMetaKeyStates(states);
}
@AnyThread
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- try {
- mIInputContext.deleteSurroundingText(beforeLength, afterLength);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.deleteSurroundingText(beforeLength, afterLength);
}
@AnyThread
@@ -427,12 +332,7 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- try {
- mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
}
@AnyThread
@@ -442,24 +342,13 @@ public class InputConnectionWrapper implements InputConnection {
}
@AnyThread
- @Override
public boolean performSpellCheck() {
- try {
- mIInputContext.performSpellCheck();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performSpellCheck();
}
@AnyThread
public boolean performPrivateCommand(String action, Bundle data) {
- try {
- mIInputContext.performPrivateCommand(action, data);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performPrivateCommand(action, data);
}
@AnyThread
@@ -472,13 +361,7 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- final Completable.Int value = Completable.createInt();
- try {
- mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode,
- ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return false;
- }
+ final Completable.Int value = mInvoker.requestUpdateCursorAnchorInfo(cursorUpdateMode);
return Completable.getResultOrZero(value, TAG, "requestUpdateCursorAnchorInfo()",
mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
}
@@ -506,7 +389,7 @@ public class InputConnectionWrapper implements InputConnection {
}
if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService == null) {
// This basically should not happen, because it's the caller of this method.
return false;
@@ -514,12 +397,7 @@ public class InputConnectionWrapper implements InputConnection {
inputMethodService.exposeContent(inputContentInfo, this);
}
- final Completable.Int value = Completable.createInt();
- try {
- mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return false;
- }
+ final Completable.Int value = mInvoker.commitContent(inputContentInfo, flags, opts);
return Completable.getResultOrZero(
value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
}
@@ -529,12 +407,7 @@ public class InputConnectionWrapper implements InputConnection {
*/
@AnyThread
public boolean setImeConsumesInput(boolean imeConsumesInput) {
- try {
- mIInputContext.setImeConsumesInput(imeConsumesInput);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.setImeConsumesInput(imeConsumesInput);
}
@AnyThread
@@ -545,7 +418,7 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
@Override
public String toString() {
- return "InputConnectionWrapper{idHash=#"
+ return "RemoteInputConnection{idHash=#"
+ Integer.toHexString(System.identityHashCode(this))
+ " mMissingMethods="
+ InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 3bde6fa6913d..1d07a0330bc5 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -26,8 +26,10 @@ import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -121,11 +123,37 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
}
builder.append(", metered=").append(mMetered);
builder.append(", defaultNetwork=").append(mDefaultNetwork);
- // TODO(180557699): Print a human readable string for OEM managed state.
- builder.append(", oemManaged=").append(mOemManaged);
+ builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
return builder.append("}").toString();
}
+ /**
+ * Get the human readable representation of a bitfield representing the OEM managed state of a
+ * network.
+ */
+ static String getOemManagedNames(int oemManaged) {
+ if (oemManaged == OEM_NONE) {
+ return "OEM_NONE";
+ }
+ final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+ final ArrayList<String> oemManagedNames = new ArrayList<String>();
+ for (int position : bitPositions) {
+ oemManagedNames.add(nameOfOemManaged(1 << position));
+ }
+ return String.join(",", oemManagedNames);
+ }
+
+ private static String nameOfOemManaged(int oemManagedBit) {
+ switch (oemManagedBit) {
+ case OEM_PAID:
+ return "OEM_PAID";
+ case OEM_PRIVATE:
+ return "OEM_PRIVATE";
+ default:
+ return "Invalid(" + oemManagedBit + ")";
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 249154aa9129..68917a82884b 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -427,7 +427,7 @@ public class NetworkTemplate implements Parcelable {
builder.append(", subType=").append(mSubType);
}
if (mOemManaged != OEM_MANAGED_ALL) {
- builder.append(", oemManaged=").append(mOemManaged);
+ builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
}
builder.append(", subscriberIdMatchRule=")
.append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule));
@@ -781,6 +781,19 @@ public class NetworkTemplate implements Parcelable {
}
}
+ private static String getOemManagedNames(int oemManaged) {
+ switch (oemManaged) {
+ case OEM_MANAGED_ALL:
+ return "OEM_MANAGED_ALL";
+ case OEM_MANAGED_NO:
+ return "OEM_MANAGED_NO";
+ case OEM_MANAGED_YES:
+ return "OEM_MANAGED_YES";
+ default:
+ return NetworkIdentity.getOemManagedNames(oemManaged);
+ }
+ }
+
/**
* Examine the given template and normalize if it refers to a "merged"
* mobile subscriber. We pick the "lowest" merged subscriber as the primary
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fa3ff8a26862..2ced05693755 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -529,7 +529,7 @@ public class VpnService extends Service {
/**
* Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
- * and it is possible that some apps will ignore it.
+ * and it is possible that some apps will ignore it. PAC proxies are not supported.
*/
@NonNull
public Builder setHttpProxy(@NonNull ProxyInfo proxyInfo) {
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/core/java/android/net/vcn/OWNERS
+++ b/core/java/android/net/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 2c088e233f65..7f526c13a75a 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -262,7 +262,7 @@ public final class BatteryStatsManager {
/**
* Indicates that a new wifi scan has started.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportWifiScanStartedFromSource(@NonNull WorkSource ws) {
@@ -276,7 +276,7 @@ public final class BatteryStatsManager {
/**
* Indicates that an ongoing wifi scan has stopped.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportWifiScanStoppedFromSource(@NonNull WorkSource ws) {
@@ -290,7 +290,7 @@ public final class BatteryStatsManager {
/**
* Indicates that a new wifi batched scan has started.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
* @param csph Channels scanned per hour.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
@@ -306,7 +306,7 @@ public final class BatteryStatsManager {
/**
* Indicates that an ongoing wifi batched scan has stopped.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
@@ -322,7 +322,9 @@ public final class BatteryStatsManager {
*
* @return Instance of {@link CellularBatteryStats}.
*/
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.BATTERY_STATS,
+ android.Manifest.permission.UPDATE_DEVICE_STATS})
public @NonNull CellularBatteryStats getCellularBatteryStats() {
try {
return mBatteryStats.getCellularBatteryStats();
@@ -337,7 +339,9 @@ public final class BatteryStatsManager {
*
* @return Instance of {@link WifiBatteryStats}.
*/
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.BATTERY_STATS,
+ android.Manifest.permission.UPDATE_DEVICE_STATS})
public @NonNull WifiBatteryStats getWifiBatteryStats() {
try {
return mBatteryStats.getWifiBatteryStats();
@@ -350,7 +354,7 @@ public final class BatteryStatsManager {
/**
* Indicates an app acquiring full wifi lock.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
@@ -364,7 +368,7 @@ public final class BatteryStatsManager {
/**
* Indicates an app releasing full wifi lock.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
@@ -479,6 +483,63 @@ public final class BatteryStatsManager {
}
}
+ /**
+ * Indicates that a new Bluetooth LE scan has started.
+ *
+ * @param ws worksource (to be used for battery blaming).
+ * @param isUnoptimized whether or not the scan has a filter.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanStarted(@NonNull WorkSource ws, boolean isUnoptimized) {
+ try {
+ mBatteryStats.noteBleScanStarted(ws, isUnoptimized);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that an ongoing Bluetooth LE scan has stopped.
+ *
+ * @param ws worksource (to be used for battery blaming).
+ * @param isUnoptimized whether or not the scan has a filter.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanStopped(@NonNull WorkSource ws, boolean isUnoptimized) {
+ try {
+ mBatteryStats.noteBleScanStopped(ws, isUnoptimized);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that Bluetooth LE has been reset.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanReset() {
+ try {
+ mBatteryStats.noteBleScanReset();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that Bluetooth LE scan has received new results.
+ *
+ * @param ws worksource (to be used for battery blaming).
+ * @param numNewResults number of results received since last update.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanResults(@NonNull WorkSource ws, int numNewResults) {
+ try {
+ mBatteryStats.noteBleScanResults(ws, numNewResults);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private static int getDataConnectionPowerState(boolean isActive) {
// TODO: DataConnectionRealTimeInfo is under telephony package but the constants are used
// for both Wifi and mobile. It would make more sense to separate the constants to a
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f48375246616..b128f6846e82 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -82,8 +82,6 @@ public final class BatteryUsageStats implements Parcelable {
public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
- private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
-
// XML tags and attributes for BatteryUsageStats persistence
static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
static final String XML_TAG_AGGREGATE = "aggregate";
@@ -112,6 +110,8 @@ public final class BatteryUsageStats implements Parcelable {
static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+ private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+
private final int mDischargePercentage;
private final double mBatteryCapacityMah;
private final long mStatsStartTimestampMs;
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index d44b016cb5d0..2afbe8b2c4aa 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -53,6 +53,12 @@ public final class BinderProxy implements IBinder {
private static volatile Binder.ProxyTransactListener sTransactListener = null;
+ private static class BinderProxyMapSizeException extends AssertionError {
+ BinderProxyMapSizeException(String s) {
+ super(s);
+ }
+ };
+
/**
* @see {@link Binder#setProxyTransactListener(listener)}.
*/
@@ -73,7 +79,10 @@ public final class BinderProxy implements IBinder {
private static final int LOG_MAIN_INDEX_SIZE = 8;
private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
- // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+ /**
+ * Debuggable builds will throw an BinderProxyMapSizeException if the number of
+ * map entries exceeds:
+ */
private static final int CRASH_AT_SIZE = 20_000;
/**
@@ -228,7 +237,8 @@ public final class BinderProxy implements IBinder {
dumpProxyInterfaceCounts();
dumpPerUidProxyCounts();
Runtime.getRuntime().gc();
- throw new AssertionError("Binder ProxyMap has too many entries: "
+ throw new BinderProxyMapSizeException(
+ "Binder ProxyMap has too many entries: "
+ totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+ unclearedSize() + " (uncleared after GC). BinderProxy leak?");
} else if (totalSize > 3 * totalUnclearedSize / 2) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6bf394dc347b..f7b1525569fd 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1130,6 +1130,11 @@ public class Build {
* S.
*/
public static final int S = 31;
+
+ /**
+ * T.
+ */
+ public static final int T = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
@@ -1394,7 +1399,11 @@ public class Build {
public static final boolean IS_USER = "user".equals(TYPE);
/**
- * Whether this build is running inside a container.
+ * Whether this build is running on ARC, the Android Runtime for Chrome
+ * (https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md).
+ * Prior to R this was implemented as a container but from R this will be
+ * a VM. The name of the property remains ro.boot.conntainer as it is
+ * referenced in other projects.
*
* We should try to avoid checking this flag if possible to minimize
* unnecessarily diverging from non-container Android behavior.
@@ -1405,7 +1414,7 @@ public class Build {
* For higher-level behavior differences, other checks should be preferred.
* @hide
*/
- public static final boolean IS_CONTAINER =
+ public static final boolean IS_ARC =
SystemProperties.getBoolean("ro.boot.container", false);
/**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2ed0bad69460..308e6d56b96e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -189,13 +189,11 @@ public class Environment {
}
@UnsupportedAppUsage
- @Deprecated
public File getExternalStorageDirectory() {
return getExternalDirs()[0];
}
@UnsupportedAppUsage
- @Deprecated
public File getExternalStoragePublicDirectory(String type) {
return buildExternalStoragePublicDirs(type)[0];
}
@@ -698,11 +696,7 @@ public class Environment {
*
* @see #getExternalStorageState()
* @see #isExternalStorageRemovable()
- * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)},
- * {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better
- * performance.
*/
- @Deprecated
public static File getExternalStorageDirectory() {
throwIfUserRequired();
return sCurrentUser.getExternalDirs()[0];
@@ -1009,11 +1003,7 @@ public class Environment {
* @return Returns the File path for the directory. Note that this directory
* may not yet exist, so you must make sure it exists before using
* it such as with {@link File#mkdirs File.mkdirs()}.
- * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)},
- * {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better
- * performance.
*/
- @Deprecated
public static File getExternalStoragePublicDirectory(String type) {
throwIfUserRequired();
return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index be21fea1d0df..b3416e98147e 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,6 +17,7 @@
package android.os;
import android.app.Activity;
+import android.app.GameManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,8 +27,6 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -37,9 +36,6 @@ import dalvik.system.VMRuntime;
import java.io.BufferedReader;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
@@ -88,9 +84,6 @@ public class GraphicsEnvironment {
private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
- // ANGLE related properties.
- private static final String ANGLE_RULES_FILE = "a4a_rules.json";
- private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
"android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
@@ -121,6 +114,7 @@ public class GraphicsEnvironment {
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
+ private GameManager mGameManager;
private int mAngleOptInIndex = -1;
@@ -133,6 +127,8 @@ public class GraphicsEnvironment {
final ApplicationInfo appInfoWithMetaData =
getAppInfoWithMetadata(context, pm, packageName);
+ mGameManager = context.getSystemService(GameManager.class);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
@@ -151,6 +147,23 @@ public class GraphicsEnvironment {
}
/**
+ * Query to determine if the Game Mode has enabled ANGLE.
+ */
+ private boolean isAngleEnabledByGameMode(Context context, String packageName) {
+ try {
+ final boolean gameModeEnabledAngle =
+ (mGameManager != null) && mGameManager.getAngleEnabled(packageName);
+ Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
+ return gameModeEnabledAngle;
+ } catch (SecurityException e) {
+ Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled "
+ + "for package: " + packageName);
+ }
+
+ return false;
+ }
+
+ /**
* Query to determine if ANGLE should be used
*/
private boolean shouldUseAngle(Context context, Bundle coreSettings,
@@ -164,21 +177,16 @@ public class GraphicsEnvironment {
Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + devOptIn + "'");
- // We only want to use ANGLE if the app is in the allowlist, or the developer has
- // explicitly chosen something other than default driver.
- // The allowlist will be generated by the ANGLE APK at both boot time and
- // ANGLE update time. It will only include apps mentioned in the rules file.
- final boolean allowed = checkAngleAllowlist(context, coreSettings, packageName);
+ // We only want to use ANGLE if the developer has explicitly chosen something other than
+ // default driver.
final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
-
- if (allowed) {
- Log.v(TAG, "ANGLE allowlist includes " + packageName);
- }
if (requested) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
- return allowed || requested;
+ final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
+
+ return requested || gameModeEnabledAngle;
}
private int getVulkanVersion(PackageManager pm) {
@@ -475,117 +483,6 @@ public class GraphicsEnvironment {
}
/**
- * Attempt to setup ANGLE with a temporary rules file.
- * True: Temporary rules file was loaded.
- * False: Temporary rules file was *not* loaded.
- */
- private boolean setupAngleWithTempRulesFile(Context context,
- String packageName,
- String paths,
- String devOptIn) {
- /**
- * We only want to load a temp rules file for:
- * - apps that are marked 'debuggable' in their manifest
- * - devices that are running a userdebug build (ro.debuggable) or can inject libraries for
- * debugging (PR_SET_DUMPABLE).
- */
- if (!isDebuggable()) {
- Log.v(TAG, "Skipping loading temporary rules file");
- return false;
- }
-
- final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
-
- if (TextUtils.isEmpty(angleTempRules)) {
- Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
- return false;
- }
-
- Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
-
- final File tempRulesFile = new File(angleTempRules);
- if (tempRulesFile.exists()) {
- Log.i(TAG, angleTempRules + " exists, loading file.");
- try {
- final FileInputStream stream = new FileInputStream(angleTempRules);
-
- try {
- final FileDescriptor rulesFd = stream.getFD();
- final long rulesOffset = 0;
- final long rulesLength = stream.getChannel().size();
- Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
-
- setAngleInfo(paths, packageName, devOptIn, null,
- rulesFd, rulesOffset, rulesLength);
-
- stream.close();
-
- // We successfully setup ANGLE, so return with good status
- return true;
- } catch (IOException e) {
- Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
- }
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Temp ANGLE rules file not found: " + e);
- } catch (SecurityException e) {
- Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
- }
- }
-
- return false;
- }
-
- /**
- * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
- * True: APK rules file was loaded.
- * False: APK rules file was *not* loaded.
- */
- private boolean setupAngleRulesApk(String anglePkgName,
- ApplicationInfo angleInfo,
- PackageManager pm,
- String packageName,
- String paths,
- String devOptIn,
- String[] features) {
- // Pass the rules file to loader for ANGLE decisions
- try {
- final AssetManager angleAssets = pm.getResourcesForApplication(angleInfo).getAssets();
-
- try {
- final AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-
- setAngleInfo(paths, packageName, devOptIn, features, assetsFd.getFileDescriptor(),
- assetsFd.getStartOffset(), assetsFd.getLength());
-
- assetsFd.close();
-
- return true;
- } catch (IOException e) {
- Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
- + " from '" + anglePkgName + "': " + e);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
- }
-
- return false;
- }
-
- /**
- * Pull ANGLE allowlist from GlobalSettings and compare against current package
- */
- private boolean checkAngleAllowlist(Context context, Bundle bundle, String packageName) {
- final ContentResolver contentResolver = context.getContentResolver();
- final List<String> angleAllowlist =
- getGlobalSettingsString(contentResolver, bundle,
- Settings.Global.ANGLE_ALLOWLIST);
-
- if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist);
-
- return angleAllowlist.contains(packageName);
- }
-
- /**
* Pass ANGLE details down to trigger enable logic
*
* @param context
@@ -647,28 +544,21 @@ public class GraphicsEnvironment {
if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
- // If the user has set the developer option to something other than default,
- // we need to call setupAngleRulesApk() with the package name and the developer
- // option value (native/angle/other). Then later when we are actually trying to
- // load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before
- // and can confidently answer yes/no based on the previously set developer
- // option value.
- final String devOptIn = getDriverForPackage(context, bundle, packageName);
-
- if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
- // We setup ANGLE with a temp rules file, so we're done here.
- return true;
- }
-
- String[] features = getAngleEglFeatures(context, bundle);
-
- if (setupAngleRulesApk(
- anglePkgName, angleInfo, pm, packageName, paths, devOptIn, features)) {
- // ANGLE with rules is set up from the APK, hence return.
- return true;
+ // We need to call setAngleInfo() with the package name and the developer option value
+ //(native/angle/other). Then later when we are actually trying to load a driver,
+ //GraphicsEnv::getShouldUseAngle() has seen the package name before and can confidently
+ //answer yes/no based on the previously set developer option value.
+ final String devOptIn;
+ final String[] features = getAngleEglFeatures(context, bundle);
+ final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
+ if (gameModeEnabledAngle) {
+ devOptIn = ANGLE_GL_DRIVER_CHOICE_ANGLE;
+ } else {
+ devOptIn = getDriverForPackage(context, bundle, packageName);
}
+ setAngleInfo(paths, packageName, devOptIn, features);
- return false;
+ return true;
}
/**
@@ -956,7 +846,7 @@ public class GraphicsEnvironment {
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
private static native void setAngleInfo(String path, String appPackage, String devOptIn,
- String[] features, FileDescriptor rulesFd, long rulesOffset, long rulesLength);
+ String[] features);
private static native boolean getShouldUseAngle(String packageName);
private static native boolean setInjectLayersPrSetDumpable();
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index e5e9b5f6f53c..cad6e66888d9 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -316,7 +316,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
new MyReadMapCallback()));
}
}
- return EMPTY;
+ return new PersistableBundle(); // An empty mutable PersistableBundle
}
@Override
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 136e3de731a9..f9b16951f0db 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -18,6 +18,7 @@ package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.system.ErrnoException;
import android.system.Os;
@@ -93,6 +94,18 @@ public final class SharedMemory implements Parcelable, Closeable {
}
}
+ /**
+ * Creates from existing shared memory passed as {@link ParcelFileDesciptor}.
+ *
+ * @param fd File descriptor of shared memory passed as {@link #ParcelFileDescriptor}.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static @NonNull SharedMemory create(@NonNull ParcelFileDescriptor fd) {
+ return new SharedMemory(fd.getFileDescriptor());
+ }
+
private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
| OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 44c3d61b8760..7455f1fd7996 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -115,20 +115,18 @@ import java.util.function.Consumer;
*
* <pre>
* public void onCreate() {
- * if (DEVELOPER_MODE) {
- * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
- * .detectDiskReads()
- * .detectDiskWrites()
- * .detectNetwork() // or .detectAll() for all detectable problems
- * .penaltyLog()
- * .build());
- * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
- * .detectLeakedSqlLiteObjects()
- * .detectLeakedClosableObjects()
- * .penaltyLog()
- * .penaltyDeath()
- * .build());
- * }
+ * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ * .detectDiskReads()
+ * .detectDiskWrites()
+ * .detectNetwork() // or .detectAll() for all detectable problems
+ * .penaltyLog()
+ * .build());
+ * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ * .detectLeakedSqlLiteObjects()
+ * .detectLeakedClosableObjects()
+ * .penaltyLog()
+ * .penaltyDeath()
+ * .build());
* super.onCreate();
* }
* </pre>
@@ -147,9 +145,7 @@ import java.util.function.Consumer;
* <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or
* network accesses. While it does propagate its state across process boundaries when doing {@link
* android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
- * access from JNI calls won't necessarily trigger it. Future versions of Android may catch more (or
- * fewer) operations, so you should never leave StrictMode enabled in applications distributed on
- * Google Play.
+ * access from JNI calls won't necessarily trigger it.
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d7893e4bbefd..feffcbd96097 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -352,6 +352,8 @@ public abstract class Vibrator {
/**
* Vibrate constantly for the specified period of time.
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param milliseconds The number of milliseconds to vibrate.
* @deprecated Use {@link #vibrate(VibrationEffect)} instead.
*/
@@ -364,6 +366,9 @@ public abstract class Vibrator {
/**
* Vibrate constantly for the specified period of time.
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param milliseconds The number of milliseconds to vibrate.
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
* specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
@@ -398,6 +403,8 @@ public abstract class Vibrator {
* to start the repeat, or -1 to disable repeating.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param pattern an array of longs of times for which to turn the vibrator on or off.
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
@@ -423,6 +430,9 @@ public abstract class Vibrator {
* to start the repeat, or -1 to disable repeating.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param pattern an array of longs of times for which to turn the vibrator on or off.
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
@@ -450,11 +460,30 @@ public abstract class Vibrator {
}
}
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
+ * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+ */
@RequiresPermission(android.Manifest.permission.VIBRATE)
public void vibrate(VibrationEffect vibe) {
vibrate(vibe, null);
}
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
+ * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+ * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+ * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+ * vibrations associated with incoming calls.
+ */
@RequiresPermission(android.Manifest.permission.VIBRATE)
public void vibrate(VibrationEffect vibe, AudioAttributes attributes) {
vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 9385402c3d72..fbac954b0a35 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -198,9 +198,9 @@ interface IStorageManager {
void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
void fixupAppDir(in String path) = 89;
void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90;
- void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 91;
- void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 92;
- PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 93;
- boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 94;
- int getExternalStorageMountMode(int uid, in String packageName) = 95;
-}
+ PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 91;
+ void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 92;
+ void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 93;
+ int getExternalStorageMountMode(int uid, in String packageName) = 94;
+ boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
+} \ No newline at end of file
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 400b312bb913..69a09fb9b7f1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -214,7 +214,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
return AudioManager.getAudioProductStrategies().stream()
.map(strategy -> strategy.getVolumeGroupIdForAudioAttributes(
- AudioProductStrategy.sDefaultAttributes))
+ AudioProductStrategy.getDefaultAttributes()))
.filter(volumeGroupId -> volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP)
.findFirst()
.orElse(AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 376d9421e8d3..b3d60a58410f 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8594,6 +8594,12 @@ public final class ContactsContract {
* <p>
* Contacts-specific settings for various {@link Account}'s.
* </p>
+ * <p>
+ * A settings entry for an account is created automatically when a raw contact or group
+ * is inserted that references it. Settings entries cannot be deleted as long as raw
+ * contacts or groups continue to reference it; in order to delete a settings entry all
+ * raw contacts and groups referencing the account must be deleted first.
+ * </p>
* <h2>Columns</h2>
* <table class="jd-sumtable">
* <tr>
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 431bf4c54b4b..880d03a98ffb 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -432,6 +432,25 @@ public final class DeviceConfig {
public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
/**
+ * Namespace for all SurfaceFlinger features that are used at the native level.
+ * These features are applied on boot or after reboot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT =
+ "surface_flinger_native_boot";
+
+ /**
+ * Namespace for swcodec native related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
+
+
+ /**
* Namespace for System UI related features.
*
* @hide
@@ -597,6 +616,14 @@ public final class DeviceConfig {
@TestApi
public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
+ /**
+ * Namespace for App Compat Overrides related features.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
@@ -767,7 +794,7 @@ public final class DeviceConfig {
}
/**
- * Create a new property with the the provided name and value in the provided namespace, or
+ * Create a new property with the provided name and value in the provided namespace, or
* update the value of such a property if it already exists. The same name can exist in multiple
* namespaces and might have different values in any or all namespaces.
* <p>
@@ -817,6 +844,22 @@ public final class DeviceConfig {
}
/**
+ * Delete a property with the provided name and value in the provided namespace
+ *
+ * @param namespace The namespace containing the property to create or update.
+ * @param name The name of the property to create or update.
+ * @return True if the property was deleted or it did not exist in the first place.
+ * False if the storage implementation throws errors.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ return Settings.Config.deleteString(contentResolver, namespace, name);
+ }
+
+ /**
* Reset properties to their default values by removing the underlying values.
* <p>
* The method accepts an optional namespace parameter. If provided, only properties set within
@@ -851,32 +894,31 @@ public final class DeviceConfig {
/**
* Disables or re-enables bulk modifications ({@link #setProperties(Properties)}) to device
* config values. This is intended for use during tests to prevent a sync operation clearing
- * config values, which could influence the outcome of the tests, i.e. by changing behavior.
+ * config values which could influence the outcome of the tests, i.e. by changing behavior.
*
* @param syncDisabledMode the mode to use, see {@link Settings.Config#SYNC_DISABLED_MODE_NONE},
* {@link Settings.Config#SYNC_DISABLED_MODE_PERSISTENT} and {@link
* Settings.Config#SYNC_DISABLED_MODE_UNTIL_REBOOT}
*
- * @see #isSyncDisabled()
+ * @see #getSyncDisabledMode()
* @hide
*/
@RequiresPermission(WRITE_DEVICE_CONFIG)
- public static void setSyncDisabled(@SyncDisabledMode int syncDisabledMode) {
+ public static void setSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- Settings.Config.setSyncDisabled(contentResolver, syncDisabledMode);
+ Settings.Config.setSyncDisabledMode(contentResolver, syncDisabledMode);
}
/**
- * Returns the current state of sync disabling, {@code true} when disabled, {@code false}
- * otherwise.
+ * Returns the current mode of sync disabling.
*
- * @see #setSyncDisabled(int)
+ * @see #setSyncDisabledMode(int)
* @hide
*/
@RequiresPermission(WRITE_DEVICE_CONFIG)
- public static boolean isSyncDisabled() {
+ public static @SyncDisabledMode int getSyncDisabledMode() {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- return Settings.Config.isSyncDisabled(contentResolver);
+ return Settings.Config.getSyncDisabledMode(contentResolver);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ac520e8b3dec..88e7ffc18ec2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -299,8 +299,8 @@ public final class Settings {
public static final String KEY_CONFIG_SET_ALL_RETURN = "config_set_all_return";
/** @hide */
- public static final String KEY_CONFIG_IS_SYNC_DISABLED_RETURN =
- "config_is_sync_disabled_return";
+ public static final String KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN =
+ "config_get_sync_disabled_mode_return";
/**
* An int extra specifying a subscription ID.
@@ -2439,13 +2439,15 @@ public final class Settings {
public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
/** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */
- public static final String CALL_METHOD_SET_SYNC_DISABLED_CONFIG = "SET_SYNC_DISABLED_config";
+ public static final String CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG =
+ "SET_SYNC_DISABLED_MODE_config";
/**
- * @hide - Private call() method to return whether syncs are disabled for the 'configuration'
- * table
+ * @hide - Private call() method to return the current mode of sync disabling for the
+ * 'configuration' table
*/
- public static final String CALL_METHOD_IS_SYNC_DISABLED_CONFIG = "IS_SYNC_DISABLED_config";
+ public static final String CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG =
+ "GET_SYNC_DISABLED_MODE_config";
/** @hide - Private call() method to register monitor callback for 'configuration' table */
public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG =
@@ -2768,6 +2770,7 @@ public final class Settings {
// for the fast path of retrieving settings.
private final String mCallGetCommand;
private final String mCallSetCommand;
+ private final String mCallDeleteCommand;
private final String mCallListCommand;
private final String mCallSetAllCommand;
@@ -2779,17 +2782,19 @@ public final class Settings {
private GenerationTracker mGenerationTracker;
<T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
- this(uri, getCommand, setCommand, null, null, providerHolder,
+ String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
+ Class<T> callerClass) {
+ this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
callerClass);
}
private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, String listCommand, String setAllCommand,
+ String setCommand, String deleteCommand, String listCommand, String setAllCommand,
ContentProviderHolder providerHolder, Class<T> callerClass) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
+ mCallDeleteCommand = deleteCommand;
mCallListCommand = listCommand;
mCallSetAllCommand = setAllCommand;
mProviderHolder = providerHolder;
@@ -2847,6 +2852,20 @@ public final class Settings {
}
}
+ public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+ IContentProvider cp = mProviderHolder.getProvider(cr);
+ cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
+ return false;
+ }
+ return true;
+ }
+
@UnsupportedAppUsage
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
// Check if the target settings key is readable. Reject if the caller is not system and
@@ -3309,6 +3328,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SYSTEM,
CALL_METHOD_PUT_SYSTEM,
+ CALL_METHOD_DELETE_SYSTEM,
sProviderHolder,
System.class);
@@ -4698,7 +4718,7 @@ public final class Settings {
*
* @hide
*/
- @Readable
+ @Readable(maxTargetSdk = Build.VERSION_CODES.R)
public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
/**
@@ -5629,6 +5649,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE,
+ CALL_METHOD_DELETE_SECURE,
sProviderHolder,
Secure.class);
@@ -10978,6 +10999,9 @@ public final class Settings {
* <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV} Upon going to sleep, device
* sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio
* system via {@code <System Audio Mode Request>}.</li>
+ * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM} Upon going to
+ * sleep, sends {@code <Standby>} to TV and Audio system. Upon waking up, device attempts
+ * to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
* <li>{@link HdmiControlManager#POWER_CONTROL_MODE_BROADCAST} Upon going to sleep,
* device sends {@code <Standby>} to all devices in the network. Upon waking up, device
* attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
@@ -13670,13 +13694,6 @@ public final class Settings {
"angle_gl_driver_selection_values";
/**
- * List of package names that should check ANGLE rules
- * @hide
- */
- @Readable
- public static final String ANGLE_ALLOWLIST = "angle_allowlist";
-
- /**
* Lists of ANGLE EGL features for debugging.
* Each list of features is separated by a comma, each feature in each list is separated by
* a colon.
@@ -14324,8 +14341,11 @@ public final class Settings {
"are_user_disabled_hdr_formats_allowed";
/**
- * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are disabled
- * currently. The value is boolean (1 or 0). The value '1' means that {@link
+ * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are currently
+ * persistently disabled. This is only used for the {@link
+ * Config#SYNC_DISABLED_MODE_PERSISTENT persistent} mode, {@link
+ * Config#SYNC_DISABLED_MODE_UNTIL_REBOOT until_reboot} mode is not stored in settings.
+ * The value is boolean (1 or 0). The value '1' means that {@link
* DeviceConfig#setProperties(DeviceConfig.Properties)} will return {@code false}.
*
* @hide
@@ -14898,6 +14918,33 @@ public final class Settings {
"max_sound_trigger_detection_service_ops_per_day";
/**
+ * Setting indicating the name of the Wear OS app package containing the device's sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_PACKAGE_NAME =
+ "clockwork_sysui_package_name";
+
+ /**
+ * Setting indicating the name of the main activity of the Wear OS sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME =
+ "clockwork_sysui_main_activity_name";
+
+ /**
+ * Setting to determine if the Clockwork Home application is ready.
+ *
+ * <p>
+ * Set to 1 when the Clockwork Home application has finished starting up.
+ * </p>
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_HOME_READY = "clockwork_home_ready";
+
+ /**
* Indicates whether aware is available in the current location.
* @hide
*/
@@ -14956,6 +15003,15 @@ public final class Settings {
public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
/**
+ * Global settings that shouldn't be persisted.
+ *
+ * @hide
+ */
+ public static final String[] TRANSIENT_SETTINGS = {
+ CLOCKWORK_HOME_READY,
+ };
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -14977,6 +15033,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL,
+ CALL_METHOD_DELETE_GLOBAL,
sProviderHolder,
Global.class);
@@ -16158,6 +16215,445 @@ public final class Settings {
* @hide
*/
public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
+
+ /**
+ * Settings migrated from Wear OS settings provider.
+ * @hide
+ */
+ public static class Wearable {
+ /**
+ * Whether the user has any pay tokens on their watch.
+ * @hide
+ */
+ public static final String HAS_PAY_TOKENS = "has_pay_tokens";
+
+ /**
+ * Gcm checkin timeout in minutes.
+ * @hide
+ */
+ public static final String GMS_CHECKIN_TIMEOUT_MIN = "gms_checkin_timeout_min";
+
+ /**
+ * If hotword detection should be enabled.
+ * @hide
+ */
+ public static final String HOTWORD_DETECTION_ENABLED = "hotword_detection_enabled";
+
+ /**
+ * Whether Smart Replies are enabled within Wear.
+ * @hide
+ */
+ public static final String SMART_REPLIES_ENABLED = "smart_replies_enabled";
+
+ /**
+ * The default vibration pattern.
+ * @hide
+ */
+ public static final String DEFAULT_VIBRATION = "default_vibration";
+
+ /**
+ * If FLP should obtain location data from the paired device.
+ * @hide
+ */
+ public static final String OBTAIN_PAIRED_DEVICE_LOCATION =
+ "obtain_paired_device_location";
+
+ /**
+ * Whether the device is in retail mode.
+ * @hide
+ */
+ public static final String RETAIL_MODE = "retail_mode";
+
+ // Possible retail mode states
+ /** @hide */
+ public static final int RETAIL_MODE_CONSUMER = 0;
+ /** @hide */
+ public static final int RETAIL_MODE_RETAIL = 1;
+
+ /**
+ * The play store availability.
+ * @hide
+ */
+ public static final String PLAY_STORE_AVAILABILITY = "play_store_availability";
+
+ // Possible play store availability states
+ /** @hide */
+ public static final int PLAY_STORE_AVAILABILITY_UNKNOWN = 0;
+ /** @hide */
+ public static final int PLAY_STORE_AVAILABLE = 1;
+ /** @hide */
+ public static final int PLAY_STORE_UNAVAILABLE = 2;
+
+ /**
+ * Whether the bug report is enabled.
+ * @hide
+ */
+ public static final String BUG_REPORT = "bug_report";
+
+ // Possible bug report states
+ /** @hide */
+ public static final int BUG_REPORT_DISABLED = 0;
+ /** @hide */
+ public static final int BUG_REPORT_ENABLED = 1;
+
+ /**
+ * The enabled/disabled state of the SmartIlluminate.
+ * @hide
+ */
+ public static final String SMART_ILLUMINATE_ENABLED = "smart_illuminate_enabled";
+
+ /**
+ * Whether automatic time is enabled on the watch.
+ * @hide
+ */
+ public static final String CLOCKWORK_AUTO_TIME = "clockwork_auto_time";
+
+ // Possible clockwork auto time states
+ /** @hide */
+ public static final int SYNC_TIME_FROM_PHONE = 0;
+ /** @hide */
+ public static final int SYNC_TIME_FROM_NETWORK = 1;
+ /** @hide */
+ public static final int AUTO_TIME_OFF = 2;
+ /** @hide */
+ public static final int INVALID_AUTO_TIME_STATE = 3;
+
+
+ /**
+ * Whether automatic time zone is enabled on the watch.
+ * @hide
+ */
+ public static final String CLOCKWORK_AUTO_TIME_ZONE = "clockwork_auto_time_zone";
+
+ // Possible clockwork auto time zone states
+ /** @hide */
+ public static final int SYNC_TIME_ZONE_FROM_PHONE = 0;
+ /** @hide */
+ public static final int SYNC_TIME_ZONE_FROM_NETWORK = 1;
+ /** @hide */
+ public static final int AUTO_TIME_ZONE_OFF = 2;
+ /** @hide */
+ public static final int INVALID_AUTO_TIME_ZONE_STATE = 3;
+
+ /**
+ * Whether 24 hour time format is enabled on the watch.
+ * @hide
+ */
+ public static final String CLOCKWORK_24HR_TIME = "clockwork_24hr_time";
+
+ /**
+ * Whether the auto wifi toggle setting is enabled.
+ * @hide
+ */
+ public static final String AUTO_WIFI = "auto_wifi";
+
+ // Possible force wifi on states
+ /** @hide */
+ public static final int AUTO_WIFI_DISABLED = 0;
+ /** @hide */
+ public static final int AUTO_WIFI_ENABLED = 1;
+
+ /**
+ * The number of minutes after the WiFi enters power save mode.
+ * @hide
+ */
+ public static final String WIFI_POWER_SAVE = "wifi_power_save";
+
+ /**
+ * The time at which we should no longer skip the wifi requirement check (we skip the
+ * wifi requirement until this time). The time is in millis since epoch.
+ * @hide
+ */
+ public static final String ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS =
+ "alt_bypass_wifi_requirement_time_millis";
+
+ /**
+ * Whether or not Up/Down Gestures are enabled.
+ * @hide
+ */
+ public static final String UPDOWN_GESTURES_ENABLED = "updown_gestures_enabled";
+
+ /**
+ * Whether the setup was skipped.
+ * @hide
+ */
+ public static final String SETUP_SKIPPED = "setup_skipped";
+
+ // Possible setup_skipped states
+ /** @hide */
+ public static final int SETUP_SKIPPED_UNKNOWN = 0;
+ /** @hide */
+ public static final int SETUP_SKIPPED_YES = 1;
+ /** @hide */
+ public static final int SETUP_SKIPPED_NO = 2;
+
+ /**
+ * The last requested call forwarding action.
+ * @hide
+ */
+ public static final String LAST_CALL_FORWARD_ACTION = "last_call_forward_action";
+
+ // Possible call forwarding actions
+ /** @hide */
+ public static final int CALL_FORWARD_ACTION_ON = 1;
+ /** @hide */
+ public static final int CALL_FORWARD_ACTION_OFF = 2;
+ /** @hide */
+ public static final int CALL_FORWARD_NO_LAST_ACTION = -1;
+
+ // Stem button settings.
+ /** @hide */
+ public static final String STEM_1_TYPE = "STEM_1_TYPE";
+ /** @hide */
+ public static final String STEM_1_DATA = "STEM_1_DATA";
+ /** @hide */
+ public static final String STEM_1_DEFAULT_DATA = "STEM_1_DEFAULT_DATA";
+ /** @hide */
+ public static final String STEM_2_TYPE = "STEM_2_TYPE";
+ /** @hide */
+ public static final String STEM_2_DATA = "STEM_2_DATA";
+ /** @hide */
+ public static final String STEM_2_DEFAULT_DATA = "STEM_2_DEFAULT_DATA";
+ /** @hide */
+ public static final String STEM_3_TYPE = "STEM_3_TYPE";
+ /** @hide */
+ public static final String STEM_3_DATA = "STEM_3_DATA";
+ /** @hide */
+ public static final String STEM_3_DEFAULT_DATA = "STEM_3_DEFAULT_DATA";
+
+ // Stem types
+ /** @hide */
+ public static final int STEM_TYPE_UNKNOWN = -1;
+ /** @hide */
+ public static final int STEM_TYPE_APP_LAUNCH = 0;
+ /** @hide */
+ public static final int STEM_TYPE_CONTACT_LAUNCH = 1;
+
+ /**
+ * If the device should be muted when off body.
+ * @hide
+ */
+ public static final String MUTE_WHEN_OFF_BODY_ENABLED = "obtain_mute_when_off_body";
+
+ /**
+ * Wear OS version string.
+ * @hide
+ */
+ public static final String WEAR_OS_VERSION_STRING = "wear_os_version_string";
+
+ /**
+ * If an alternate launcher is enabled.
+ * @hide
+ */
+ public static final String ALTERNATE_LAUNCHER_ENABLED = "alternate_launcher_enabled";
+
+ /**
+ * How round the corners of square screens are.
+ * @hide
+ */
+ public static final String CORNER_ROUNDNESS = "corner_roundness";
+
+ /**
+ * Whether the physical button has been set.
+ * @hide
+ */
+ public static final String BUTTON_SET = "button_set";
+
+ /**
+ * Whether there is a side button.
+ * @hide
+ */
+ public static final String SIDE_BUTTON = "side_button";
+
+ /**
+ * The android wear system version.
+ * @hide
+ */
+ public static final String ANDROID_WEAR_VERSION = "android_wear_version";
+
+ /**
+ * The wear system capabiltiies.
+ * @hide
+ */
+ public static final String SYSTEM_CAPABILITIES = "system_capabilities";
+
+ /**
+ * The android wear system edition.
+ * @hide
+ */
+ public static final String SYSTEM_EDITION = "android_wear_system_edition";
+
+ /**
+ * The Wear platform MR number.
+ * @hide
+ */
+ public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
+
+ /**
+ * The bluetooth settings storing duplicate address of companion device.
+ * @hide
+ */
+ public static final String COMPANION_BT_ADDRESS_DUAL = "companion_bt_address_dual";
+
+ /**
+ * The offset of the visible screen from the display bottom (overscan bottom).
+ * @hide
+ */
+ public static final String BOTTOM_OFFSET = "bottom_offset";
+
+ /**
+ * The shape of the display.
+ * @hide
+ */
+ public static final String DISPLAY_SHAPE = "display_shape";
+
+ // Possible display shapes
+ /** @hide */
+ public static final int DISPLAY_SHAPE_SQUARE = 0;
+ /** @hide */
+ public static final int DISPLAY_SHAPE_ROUND = 1;
+
+ /**
+ * The different levels of screen brightness the user can select.
+ * @hide
+ */
+ public static final String SCREEN_BRIGHTNESS_LEVEL = "screen_brightness_level";
+
+ /**
+ * The mobile signal detector setting.
+ * @hide
+ */
+ public static final String MOBILE_SIGNAL_DETECTOR = "mobile_signal_detector";
+
+
+ /**
+ * Whether ambient is currently enabled.
+ * @hide
+ */
+ public static final String AMBIENT_ENABLED = "ambient_enabled";
+
+ /**
+ * Whether ambient tilt to wake is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_TILT_TO_WAKE = "ambient_tilt_to_wake";
+
+ /**
+ * Whether ambient low bit mode is enabled by developer options.
+ * @hide
+ */
+ public static final String AMBIENT_LOW_BIT_ENABLED_DEV = "ambient_low_bit_enabled_dev";
+
+ /**
+ * Whether ambient touch to wake is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_TOUCH_TO_WAKE = "ambient_touch_to_wake";
+
+ /**
+ * Whether ambient tilt to bright is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright";
+
+ /**
+ * Whether the current watchface is decomposable.
+ * @hide
+ */
+ public static final String DECOMPOSABLE_WATCHFACE = "current_watchface_decomposable";
+
+ /**
+ * Whether to force ambient when docked.
+ * @hide
+ */
+ public static final String AMBIENT_FORCE_WHEN_DOCKED = "ambient_force_when_docked";
+
+ /**
+ * The id of the gesture sensor.
+ * @hide
+ */
+ public static final String AMBIENT_GESTURE_SENSOR_ID = "ambient_gesture_sensor_id";
+
+ /**
+ * Whether the ambient low bit mode is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_LOW_BIT_ENABLED = "ambient_low_bit_enabled";
+
+ /**
+ * The timeout duration in minutes of ambient mode when plugged in.
+ * @hide
+ */
+ public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min";
+
+ /**
+ * The companion device's bluetooth address.
+ * @hide
+ */
+ public static final String COMPANION_ADDRESS = "companion_address";
+
+ /**
+ * What OS does paired device has.
+ * @hide
+ */
+ public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type";
+
+ // Possible values of PAIRED_DEVICE_OS_TYPE
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_UNKNOWN = 0;
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_ANDROID = 1;
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_IOS = 2;
+
+ /**
+ * The bluetooth settings selected BLE role for the companion.
+ * @hide
+ */
+ public static final String COMPANION_BLE_ROLE = "companion_ble_role";
+
+ // Possible values of COMPANION_BLE_ROLE
+ /** @hide */
+ public static final int BLUETOOTH_ROLE_CENTRAL = 1;
+ /** @hide */
+ public static final int BLUETOOTH_ROLE_PERIPHERAL = 2;
+
+ /**
+ * The bluetooth settings stored companion device name.
+ * @hide
+ */
+ public static final String COMPANION_NAME = "companion_bt_name";
+
+ /**
+ * The user's last setting for hfp client.
+ * @hide
+ */
+ public static final String USER_HFP_CLIENT_SETTING = "user_hfp_client_setting";
+
+ // Possible hfp client user setting values
+ /** @hide */
+ public static final int HFP_CLIENT_UNSET = 0;
+ /** @hide */
+ public static final int HFP_CLIENT_ENABLED = 1;
+ /** @hide */
+ public static final int HFP_CLIENT_DISABLED = 2;
+
+ /**
+ * The current HFP client profile setting.
+ * @hide
+ */
+ public static final String HFP_CLIENT_PROFILE_ENABLED = "hfp_client_profile_enabled";
+
+ /**
+ * The companion phone's android version.
+ * @hide
+ */
+ public static final String COMPANION_OS_VERSION = "wear_companion_os_version";
+
+ // Companion os version constants
+ /** @hide */
+ public static final int COMPANION_OS_VERSION_UNDEFINED = -1;
+ }
}
/**
@@ -16209,6 +16705,7 @@ public final class Settings {
DeviceConfig.CONTENT_URI,
CALL_METHOD_GET_CONFIG,
CALL_METHOD_PUT_CONFIG,
+ CALL_METHOD_DELETE_CONFIG,
CALL_METHOD_LIST_CONFIG,
CALL_METHOD_SET_ALL_CONFIG,
sProviderHolder,
@@ -16318,6 +16815,26 @@ public final class Settings {
}
/**
+ * Delete a name/value pair from the database for the specified namespace.
+ *
+ * @param resolver to access the database with.
+ * @param namespace to delete the name/value pair from.
+ * @param name to delete.
+ * @return true if the value was deleted, false on database errors. If the name/value pair
+ * did not exist, return True.
+ *
+ * @see #resetToDefaults(ContentResolver, int, String)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull String name) {
+ return sNameValueCache.deleteStringForUser(resolver,
+ createCompositeName(namespace, name), resolver.getUserId());
+ }
+
+ /**
* Reset the values to their defaults.
* <p>
* The method accepts an optional prefix parameter. If provided, only pairs with a name that
@@ -16350,47 +16867,47 @@ public final class Settings {
}
/**
- * Bridge method between {@link DeviceConfig#setSyncDisabled(int)} and the
+ * Bridge method between {@link DeviceConfig#setSyncDisabledMode(int)} and the
* {@link com.android.providers.settings.SettingsProvider} implementation.
*
* @hide
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static void setSyncDisabled(
+ static void setSyncDisabledMode(
@NonNull ContentResolver resolver, @SyncDisabledMode int disableSyncMode) {
try {
Bundle args = new Bundle();
args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getAttributionSource(),
- sProviderHolder.mUri.getAuthority(), CALL_METHOD_SET_SYNC_DISABLED_CONFIG,
- null, args);
+ cp.call(resolver.getAttributionSource(), sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG, null, args);
} catch (RemoteException e) {
- Log.w(TAG, "Can't set sync disabled " + DeviceConfig.CONTENT_URI, e);
+ Log.w(TAG, "Can't set sync disabled mode " + DeviceConfig.CONTENT_URI, e);
}
}
/**
- * Bridge method between {@link DeviceConfig#isSyncDisabled()} and the
+ * Bridge method between {@link DeviceConfig#getSyncDisabledMode()} and the
* {@link com.android.providers.settings.SettingsProvider} implementation.
*
* @hide
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static boolean isSyncDisabled(@NonNull ContentResolver resolver) {
+ static int getSyncDisabledMode(@NonNull ContentResolver resolver) {
try {
Bundle args = Bundle.EMPTY;
IContentProvider cp = sProviderHolder.getProvider(resolver);
Bundle bundle = cp.call(resolver.getAttributionSource(),
- sProviderHolder.mUri.getAuthority(), CALL_METHOD_IS_SYNC_DISABLED_CONFIG,
+ sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG,
null, args);
- return bundle.getBoolean(KEY_CONFIG_IS_SYNC_DISABLED_RETURN);
+ return bundle.getInt(KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN);
} catch (RemoteException e) {
- Log.w(TAG, "Can't query sync disabled " + DeviceConfig.CONTENT_URI, e);
+ Log.w(TAG, "Can't query sync disabled mode " + DeviceConfig.CONTENT_URI, e);
}
- return false;
+ return -1;
}
/**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index f3a8b5d79ea0..387fc39a65e4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5343,19 +5343,19 @@ public final class Telephony {
public static final String COLUMN_RCS_CONFIG = "rcs_config";
/**
- * TelephonyProvider column name for VoIMS provisioning. Default is 0.
- * <P>Type: INTEGER </P>
+ * TelephonyProvider column name for device to device sharing status.
*
* @hide
*/
- public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+ public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
/**
- * TelephonyProvider column name for device to device sharing status.
+ * TelephonyProvider column name for VoIMS provisioning. Default is 0.
+ * <P>Type: INTEGER </P>
*
* @hide
*/
- public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
+ public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
/**
* TelephonyProvider column name for information selected contacts that allow device to
@@ -5365,5 +5365,6 @@ public final class Telephony {
*/
public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
"d2d_sharing_contacts";
+
}
}
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index d15514dfb899..8eeecc293104 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -28,6 +28,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Defines actions to be applied to a {@link RemoteViews template presentation}.
@@ -94,7 +95,7 @@ public final class BatchUpdates implements Parcelable {
*/
public Builder updateTemplate(@NonNull RemoteViews updates) {
throwIfDestroyed();
- mUpdates = Preconditions.checkNotNull(updates);
+ mUpdates = Objects.requireNonNull(updates);
return this;
}
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index e3e884406188..5697e24a2925 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -32,6 +32,7 @@ import com.android.internal.util.Preconditions;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -147,9 +148,9 @@ public final class CharSequenceTransformation extends InternalTransformation imp
public Builder addField(@NonNull AutofillId id, @NonNull Pattern regex,
@NonNull String subst) {
throwIfDestroyed();
- Preconditions.checkNotNull(id);
- Preconditions.checkNotNull(regex);
- Preconditions.checkNotNull(subst);
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(regex);
+ Objects.requireNonNull(subst);
mFields.put(id, new Pair<>(regex, subst));
return this;
diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java
index c7dc15a8c774..92952cb7dc24 100644
--- a/core/java/android/service/autofill/CompositeUserData.java
+++ b/core/java/android/service/autofill/CompositeUserData.java
@@ -25,10 +25,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Objects;
/**
* Holds both a generic and package-specific userData used for
@@ -103,7 +102,7 @@ public final class CompositeUserData implements FieldClassificationUserData, Par
@Nullable
@Override
public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
- Preconditions.checkNotNull(categoryId);
+ Objects.requireNonNull(categoryId);
final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
return null;
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 6df01545c09f..f3f912bb3a5b 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -32,6 +32,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Defines a custom description for the autofill save UI.
@@ -157,7 +158,7 @@ public final class CustomDescription implements Parcelable {
* {@link android.os.Build.VERSION_CODES#P} or higher).
*/
public Builder(@NonNull RemoteViews parentPresentation) {
- mPresentation = Preconditions.checkNotNull(parentPresentation);
+ mPresentation = Objects.requireNonNull(parentPresentation);
}
/**
@@ -276,7 +277,7 @@ public final class CustomDescription implements Parcelable {
throwIfDestroyed();
Preconditions.checkArgument((condition instanceof InternalValidator),
"not provided by Android System: %s", condition);
- Preconditions.checkNotNull(updates);
+ Objects.requireNonNull(updates);
if (mUpdates == null) {
mUpdates = new ArrayList<>();
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 87c0d9d3e08c..8539bf58da27 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -34,6 +34,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -280,7 +281,7 @@ public final class Dataset implements Parcelable {
* @param presentation The presentation used to visualize this dataset.
*/
public Builder(@NonNull RemoteViews presentation) {
- Preconditions.checkNotNull(presentation, "presentation must be non-null");
+ Objects.requireNonNull(presentation, "presentation must be non-null");
mPresentation = presentation;
}
@@ -296,7 +297,7 @@ public final class Dataset implements Parcelable {
*/
@SystemApi
public Builder(@NonNull InlinePresentation inlinePresentation) {
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
mInlinePresentation = inlinePresentation;
}
@@ -321,7 +322,7 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setInlinePresentation(
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
mInlinePresentation = inlinePresentation;
return this;
}
@@ -343,8 +344,8 @@ public final class Dataset implements Parcelable {
@NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
- Preconditions.checkNotNull(inlineTooltipPresentation,
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
+ Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation must be non-null");
mInlinePresentation = inlinePresentation;
mInlineTooltipPresentation = inlineTooltipPresentation;
@@ -540,7 +541,7 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, null, null);
return this;
}
@@ -613,7 +614,7 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter, @NonNull RemoteViews presentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, null,
new DatasetFieldFilter(filter));
return this;
@@ -644,8 +645,8 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
return this;
}
@@ -676,9 +677,9 @@ public final class Dataset implements Parcelable {
@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
- Preconditions.checkNotNull(inlineTooltipPresentation,
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
inlineTooltipPresentation, null);
@@ -722,8 +723,8 @@ public final class Dataset implements Parcelable {
@Nullable Pattern filter, @NonNull RemoteViews presentation,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
new DatasetFieldFilter(filter));
return this;
@@ -761,9 +762,9 @@ public final class Dataset implements Parcelable {
@NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
- Preconditions.checkNotNull(inlineTooltipPresentation,
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
inlineTooltipPresentation, new DatasetFieldFilter(filter));
@@ -800,7 +801,7 @@ public final class Dataset implements Parcelable {
@Nullable AutofillValue value, @Nullable Pattern filter,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
new DatasetFieldFilter(filter));
return this;
@@ -819,7 +820,7 @@ public final class Dataset implements Parcelable {
@Nullable InlinePresentation inlinePresentation,
@Nullable InlinePresentation tooltip,
@Nullable DatasetFieldFilter filter) {
- Preconditions.checkNotNull(id, "id cannot be null");
+ Objects.requireNonNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
if (existingIdx >= 0) {
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 338ba749eb79..734085737159 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -29,9 +29,8 @@ import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
import android.widget.TextView;
-import com.android.internal.util.Preconditions;
-
import java.util.Date;
+import java.util.Objects;
/**
* Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of a field
@@ -57,8 +56,8 @@ public final class DateTransformation extends InternalTransformation implements
* @param dateFormat object used to transform the date value of the field to a String.
*/
public DateTransformation(@NonNull AutofillId id, @NonNull DateFormat dateFormat) {
- mFieldId = Preconditions.checkNotNull(id);
- mDateFormat = Preconditions.checkNotNull(dateFormat);
+ mFieldId = Objects.requireNonNull(id);
+ mDateFormat = Objects.requireNonNull(dateFormat);
}
/** @hide */
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 707bab1ec177..6f7808ee181a 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -27,9 +27,8 @@ import android.os.Parcelable;
import android.util.Log;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
import java.util.Date;
+import java.util.Objects;
/**
* Sanitizes a date {@link AutofillValue} using a {@link DateFormat}.
@@ -52,7 +51,7 @@ public final class DateValueSanitizer extends InternalSanitizer implements Sanit
* @param dateFormat date format applied to the actual date value of an input field.
*/
public DateValueSanitizer(@NonNull DateFormat dateFormat) {
- mDateFormat = Preconditions.checkNotNull(dateFormat);
+ mDateFormat = Objects.requireNonNull(dateFormat);
}
/** @hide */
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 5bf56cb9c1b5..5302b9b18441 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -22,12 +22,11 @@ import android.annotation.NonNull;
import android.os.Parcel;
import android.view.autofill.Helper;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
/**
* Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
@@ -39,7 +38,7 @@ public final class FieldClassification {
/** @hide */
public FieldClassification(@NonNull ArrayList<Match> matches) {
- mMatches = Preconditions.checkNotNull(matches);
+ mMatches = Objects.requireNonNull(matches);
Collections.sort(mMatches, new Comparator<Match>() {
@Override
public int compare(Match o1, Match o2) {
@@ -113,7 +112,7 @@ public final class FieldClassification {
/** @hide */
public Match(String categoryId, float score) {
- mCategoryId = Preconditions.checkNotNull(categoryId);
+ mCategoryId = Objects.requireNonNull(categoryId);
mScore = score;
}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 62becc507404..af846b62ae2c 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -96,6 +96,8 @@ public final class FillRequest implements Parcelable {
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
+ // The flag value 0x20 has been defined in AutofillManager.
+
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 740ae260999f..970cb1888317 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,6 +41,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Response for an {@link
@@ -598,7 +599,7 @@ public final class FillResponse implements Parcelable {
public Builder setHeader(@NonNull RemoteViews header) {
throwIfDestroyed();
throwIfAuthenticationCalled();
- mHeader = Preconditions.checkNotNull(header);
+ mHeader = Objects.requireNonNull(header);
return this;
}
@@ -630,7 +631,7 @@ public final class FillResponse implements Parcelable {
public Builder setFooter(@NonNull RemoteViews footer) {
throwIfDestroyed();
throwIfAuthenticationCalled();
- mFooter = Preconditions.checkNotNull(footer);
+ mFooter = Objects.requireNonNull(footer);
return this;
}
@@ -649,7 +650,7 @@ public final class FillResponse implements Parcelable {
public Builder setUserData(@NonNull UserData userData) {
throwIfDestroyed();
throwIfAuthenticationCalled();
- mUserData = Preconditions.checkNotNull(userData);
+ mUserData = Objects.requireNonNull(userData);
return this;
}
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 974f0ead9d19..e3171594c39e 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -33,6 +33,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -127,7 +128,7 @@ public final class ImageTransformation extends InternalTransformation implements
*/
@Deprecated
public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
addOption(regex, resId);
}
@@ -143,7 +144,7 @@ public final class ImageTransformation extends InternalTransformation implements
*/
public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
@NonNull CharSequence contentDescription) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
addOption(regex, resId, contentDescription);
}
@@ -177,7 +178,7 @@ public final class ImageTransformation extends InternalTransformation implements
*/
public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
@NonNull CharSequence contentDescription) {
- addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+ addOptionInternal(regex, resId, Objects.requireNonNull(contentDescription));
return this;
}
@@ -185,7 +186,7 @@ public final class ImageTransformation extends InternalTransformation implements
@Nullable CharSequence contentDescription) {
throwIfDestroyed();
- Preconditions.checkNotNull(regex);
+ Objects.requireNonNull(regex);
Preconditions.checkArgument(resId != 0);
mOptions.add(new Option(regex, resId, contentDescription));
diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java
index 2f098e282101..d626845b3b3e 100644
--- a/core/java/android/service/autofill/NegationValidator.java
+++ b/core/java/android/service/autofill/NegationValidator.java
@@ -22,7 +22,7 @@ import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* Validator used to implement a {@code NOT} logical operation.
@@ -33,7 +33,7 @@ final class NegationValidator extends InternalValidator {
@NonNull private final InternalValidator mValidator;
NegationValidator(@NonNull InternalValidator validator) {
- mValidator = Preconditions.checkNotNull(validator);
+ mValidator = Objects.requireNonNull(validator);
}
@Override
diff --git a/core/java/android/service/autofill/RegexValidator.java b/core/java/android/service/autofill/RegexValidator.java
index 8cb67d08e8a8..00c43473ce7f 100644
--- a/core/java/android/service/autofill/RegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -25,8 +25,7 @@ import android.os.Parcelable;
import android.util.Log;
import android.view.autofill.AutofillId;
-import com.android.internal.util.Preconditions;
-
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -50,8 +49,8 @@ public final class RegexValidator extends InternalValidator implements Validator
* otherwise, it returns {@code false}.
*/
public RegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
- mId = Preconditions.checkNotNull(id);
- mRegex = Preconditions.checkNotNull(regex);
+ mId = Objects.requireNonNull(id);
+ mRegex = Objects.requireNonNull(regex);
}
/** @hide */
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 1753ecfa4e23..87a869f0ae17 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -23,7 +23,7 @@ import android.content.IntentSender;
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* Handles save requests from the {@link AutofillService} into the {@link Activity} being
@@ -70,7 +70,7 @@ public final class SaveCallback {
* or {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess(@NonNull IntentSender intentSender) {
- onSuccessInternal(Preconditions.checkNotNull(intentSender));
+ onSuccessInternal(Objects.requireNonNull(intentSender));
}
private void onSuccessInternal(@Nullable IntentSender intentSender) {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 619bfa225cf1..8edfde8c3914 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -39,6 +39,7 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Objects;
/**
* Information used to indicate that an {@link AutofillService} is interested on saving the
@@ -769,7 +770,7 @@ public final class SaveInfo implements Parcelable {
*/
public @NonNull Builder setTriggerId(@NonNull AutofillId id) {
throwIfDestroyed();
- mTriggerId = Preconditions.checkNotNull(id);
+ mTriggerId = Objects.requireNonNull(id);
return this;
}
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
index 5dd07c491980..3ad5352a1282 100644
--- a/core/java/android/service/autofill/SaveRequest.java
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -22,10 +22,9 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* This class represents a request to an {@link AutofillService
@@ -41,7 +40,7 @@ public final class SaveRequest implements Parcelable {
/** @hide */
public SaveRequest(@NonNull ArrayList<FillContext> fillContexts,
@Nullable Bundle clientState, @Nullable ArrayList<String> datasetIds) {
- mFillContexts = Preconditions.checkNotNull(fillContexts, "fillContexts");
+ mFillContexts = Objects.requireNonNull(fillContexts, "fillContexts");
mClientState = clientState;
mDatasetIds = datasetIds;
}
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index cc48fcbe734e..5bafa7a1ff54 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -26,8 +26,7 @@ import android.os.Parcelable;
import android.util.Slog;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -57,8 +56,8 @@ public final class TextValueSanitizer extends InternalSanitizer implements
* group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
*/
public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
- mRegex = Preconditions.checkNotNull(regex);
- mSubst = Preconditions.checkNotNull(subst);
+ mRegex = Objects.requireNonNull(regex);
+ mSubst = Objects.requireNonNull(subst);
}
/** @hide */
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index eaffc92c1d6b..e9d520365f3e 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -43,6 +43,7 @@ import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Defines the user data used for
@@ -108,7 +109,7 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
@Nullable
@Override
public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
- Preconditions.checkNotNull(categoryId);
+ Objects.requireNonNull(categoryId);
if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
return null;
}
@@ -296,7 +297,7 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId,
@Nullable String name, @Nullable Bundle args) {
throwIfDestroyed();
- Preconditions.checkNotNull(categoryId);
+ Objects.requireNonNull(categoryId);
if (mCategoryAlgorithms == null) {
mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount());
}
@@ -368,13 +369,13 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
}
private String checkNotEmpty(@NonNull String name, @Nullable String value) {
- Preconditions.checkNotNull(value);
+ Objects.requireNonNull(value);
Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
return value;
}
private void checkValidValue(@Nullable String value) {
- Preconditions.checkNotNull(value);
+ Objects.requireNonNull(value);
final int length = value.length();
Preconditions.checkArgumentInRange(length, getMinValueLength(),
getMaxValueLength(), "value length (" + length + ")");
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 7cd674e847fd..fd0520d87af8 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -26,9 +26,8 @@ import android.util.Pair;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
import java.util.List;
+import java.util.Objects;
/**
* Object used to interact with the autofill system.
@@ -52,7 +51,7 @@ public final class FillController {
* automatically {@link FillWindow#destroy() destroyed}.
*/
public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(values);
if (sDebug) {
Log.d(TAG, "autofill() with " + values.size() + " values");
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index d4f7e114c291..0ce040d7f862 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -36,12 +36,12 @@ import android.view.WindowManager;
import android.view.autofill.IAutofillWindowPresenter;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.Objects;
/**
* Handle to a window used to display the augmented autofill UI.
@@ -103,9 +103,9 @@ public final class FillWindow implements AutoCloseable {
Log.d(TAG, "Updating " + area + " + with " + rootView);
}
// TODO(b/123100712): add test case for null
- Preconditions.checkNotNull(area);
- Preconditions.checkNotNull(area.proxy);
- Preconditions.checkNotNull(rootView);
+ Objects.requireNonNull(area);
+ Objects.requireNonNull(area.proxy);
+ Objects.requireNonNull(rootView);
// TODO(b/123100712): must check the area is a valid object returned by
// SmartSuggestionParams, throw IAE if not
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 3c44cfd4c7f6..737c95fadccd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -54,7 +54,6 @@ import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,6 +61,7 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -544,8 +544,8 @@ public abstract class ContentCaptureService extends Service {
@Override
public void onAccept(@NonNull Executor executor,
@NonNull DataShareReadAdapter adapter) {
- Preconditions.checkNotNull(adapter);
- Preconditions.checkNotNull(executor);
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(executor);
DataShareReadAdapterDelegate delegate =
new DataShareReadAdapterDelegate(executor, adapter,
@@ -661,9 +661,9 @@ public abstract class ContentCaptureService extends Service {
DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter,
LocalDataShareAdapterResourceManager resourceManager) {
- Preconditions.checkNotNull(executor);
- Preconditions.checkNotNull(adapter);
- Preconditions.checkNotNull(resourceManager);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(resourceManager);
resourceManager.initializeForDelegate(this, adapter, executor);
mResourceManagerReference = new WeakReference<>(resourceManager);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 4fd36e590d18..91042bfa3402 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -91,7 +91,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
= "android.service.notification.NotificationAssistantService";
/**
- * Data type: int, the feedback rating score provided by user
+ * Data type: int, the feedback rating score provided by user. The score can be any integer
+ * value depends on the experimental and feedback UX design.
*/
public static final String FEEDBACK_RATING = "feedback.rating";
@@ -129,7 +130,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* A notification was posted by an app. Called before post.
*
* <p>Note: this method is only called if you don't override
- * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)} or
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
*
* @param sbn the new notification
* @return an adjustment or null to take no action, within 200ms.
@@ -139,6 +141,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
/**
* A notification was posted by an app. Called before post.
*
+ * <p>Note: this method is only called if you don't override
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
+ *
* @param sbn the new notification
* @param channel the channel the notification was posted to
* @return an adjustment or null to take no action, within 200ms.
@@ -282,7 +287,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* @param key the notification key
* @param rankingMap The current ranking map that can be used to retrieve ranking information
* for active notifications.
- * @param feedback the feedback detail
+ * @param feedback the received feedback, such as {@link #FEEDBACK_RATING rating score}
*/
public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap,
@NonNull Bundle feedback) {
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 314c97db4e7b..1e5ff3a536ce 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -20,6 +20,8 @@ import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Calendar;
import java.util.Objects;
import java.util.TimeZone;
@@ -92,16 +94,22 @@ public class ScheduleCalendar {
*/
public long getNextChangeTime(long now) {
if (mSchedule == null) return 0;
- final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute);
- final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute);
+ final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute, true);
+ final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute, false);
long nextScheduleTime = Math.min(nextStart, nextEnd);
return nextScheduleTime;
}
- private long getNextTime(long now, int hr, int min) {
- final long time = getTime(now, hr, min);
- return time <= now ? addDays(time, 1) : time;
+ private long getNextTime(long now, int hr, int min, boolean adjust) {
+ // The adjust parameter indicates whether to potentially adjust the time to the closest
+ // actual time if the indicated time is one skipped due to daylight time.
+ final long time = adjust ? getClosestActualTime(now, hr, min) : getTime(now, hr, min);
+ if (time <= now) {
+ final long tomorrow = addDays(time, 1);
+ return adjust ? getClosestActualTime(tomorrow, hr, min) : getTime(tomorrow, hr, min);
+ }
+ return time;
}
private long getTime(long millis, int hour, int min) {
@@ -119,7 +127,7 @@ public class ScheduleCalendar {
*/
public boolean isInSchedule(long time) {
if (mSchedule == null || mDays.size() == 0) return false;
- final long start = getTime(time, mSchedule.startHour, mSchedule.startMinute);
+ final long start = getClosestActualTime(time, mSchedule.startHour, mSchedule.startMinute);
long end = getTime(time, mSchedule.endHour, mSchedule.endMinute);
if (end <= start) {
end = addDays(end, 1);
@@ -134,7 +142,7 @@ public class ScheduleCalendar {
*/
public boolean isAlarmInSchedule(long alarm, long now) {
if (mSchedule == null || mDays.size() == 0) return false;
- final long start = getTime(alarm, mSchedule.startHour, mSchedule.startMinute);
+ final long start = getClosestActualTime(alarm, mSchedule.startHour, mSchedule.startMinute);
long end = getTime(alarm, mSchedule.endHour, mSchedule.endMinute);
if (end <= start) {
end = addDays(end, 1);
@@ -186,4 +194,41 @@ public class ScheduleCalendar {
mCalendar.add(Calendar.DATE, days);
return mCalendar.getTimeInMillis();
}
+
+ /**
+ * This function returns the closest "actual" time to the provided hour/minute relative to the
+ * reference time. For most times this will behave exactly the same as getTime, but for any time
+ * during the hour skipped forward for daylight savings time (for instance, 02:xx when the
+ * clock is set to 03:00 after 01:59), this method will return the time when the clock changes
+ * (in this example, 03:00).
+ *
+ * Assumptions made in this implementation:
+ * - Time is moved forward on an hour boundary (minute 0) by exactly 1hr when clocks shift
+ * - a lenient Calendar implementation will interpret 02:xx on a day when 2-3AM is skipped
+ * as 03:xx
+ * - The skipped hour is never 11PM / 23:00.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public long getClosestActualTime(long refTime, int hour, int min) {
+ long resTime = getTime(refTime, hour, min);
+ if (!mCalendar.getTimeZone().observesDaylightTime()) {
+ // Do nothing if the timezone doesn't observe daylight time at all.
+ return resTime;
+ }
+
+ // Approach to identifying whether the time is "skipped": get the result from starting with
+ // refTime and setting hour and minute, then re-extract the hour and minute of the resulting
+ // moment in time. If the hour is exactly one more than the passed-in hour and the minute is
+ // the same, then the provided hour is likely a skipped one. If the time doesn't fall into
+ // this category, return the unmodified time instead.
+ mCalendar.setTimeInMillis(resTime);
+ int resHr = mCalendar.get(Calendar.HOUR_OF_DAY);
+ int resMin = mCalendar.get(Calendar.MINUTE);
+ if (resHr == hour + 1 && resMin == min) {
+ return getTime(refTime, resHr, 0);
+ }
+ return resTime;
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index ee8353a9f203..45bdb110634c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -19,9 +19,11 @@ package android.service.notification;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -79,7 +81,7 @@ public class ZenModeConfig implements Parcelable {
public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
public static final int MAX_SOURCE = SOURCE_STAR;
- private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
+ private static final int DEFAULT_SOURCE = SOURCE_STAR;
private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
public static final String MANUAL_RULE_ID = "MANUAL_RULE";
@@ -103,14 +105,17 @@ public class ZenModeConfig implements Parcelable {
private static final boolean DEFAULT_ALLOW_MEDIA = true;
private static final boolean DEFAULT_ALLOW_SYSTEM = false;
private static final boolean DEFAULT_ALLOW_CALLS = true;
- private static final boolean DEFAULT_ALLOW_MESSAGES = false;
+ private static final boolean DEFAULT_ALLOW_MESSAGES = true;
private static final boolean DEFAULT_ALLOW_REMINDERS = false;
private static final boolean DEFAULT_ALLOW_EVENTS = false;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
- private static final boolean DEFAULT_ALLOW_CONV = false;
- private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_NONE;
+ private static final boolean DEFAULT_ALLOW_CONV = true;
+ private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
- private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
+ // Default setting here is 010011101 = 157
+ private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
+ SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT;
public static final int XML_VERSION = 8;
public static final String ZEN_TAG = "zen";
@@ -568,16 +573,22 @@ public class ZenModeConfig implements Parcelable {
// migrate old suppressed visual effects fields, if they still exist in the xml
Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
- if (allowWhenScreenOff != null) {
+ Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
+ if (allowWhenScreenOff != null || allowWhenScreenOn != null) {
+ // If either setting exists, then reset the suppressed visual effects field
+ // to 0 (all allowed) so that only the relevant bits are disallowed by
+ // the migrated settings.
readSuppressedEffects = true;
+ rt.suppressedVisualEffects = 0;
+ }
+ if (allowWhenScreenOff != null) {
if (!allowWhenScreenOff) {
rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
- | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_AMBIENT;
}
}
- Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
if (allowWhenScreenOn != null) {
- readSuppressedEffects = true;
if (!allowWhenScreenOn) {
rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
}
@@ -1001,7 +1012,7 @@ public class ZenModeConfig implements Parcelable {
builder.showBadges(
(suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
builder.showInAmbientDisplay(
- (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0);
+ (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0);
builder.showInNotificationList(
(suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
}
@@ -1085,7 +1096,7 @@ public class ZenModeConfig implements Parcelable {
boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
ZenPolicy.VISUAL_EFFECT_AMBIENT,
- isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+ isVisualEffectAllowed(SUPPRESSED_EFFECT_AMBIENT,
defaultPolicy));
if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
@@ -1120,7 +1131,7 @@ public class ZenModeConfig implements Parcelable {
}
if (suppressAmbient) {
- suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+ suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
}
if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 1fb18fab3775..29d430d8e20f 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -49,10 +49,9 @@ import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -127,8 +126,8 @@ public abstract class TextClassifierService extends Service {
public void onSuggestSelection(
TextClassificationSessionId sessionId,
TextSelection.Request request, ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
@@ -138,8 +137,8 @@ public abstract class TextClassifierService extends Service {
public void onClassifyText(
TextClassificationSessionId sessionId,
TextClassification.Request request, ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -148,8 +147,8 @@ public abstract class TextClassifierService extends Service {
public void onGenerateLinks(
TextClassificationSessionId sessionId,
TextLinks.Request request, ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -158,7 +157,7 @@ public abstract class TextClassifierService extends Service {
public void onSelectionEvent(
TextClassificationSessionId sessionId,
SelectionEvent event) {
- Preconditions.checkNotNull(event);
+ Objects.requireNonNull(event);
mMainThreadHandler.post(
() -> TextClassifierService.this.onSelectionEvent(sessionId, event));
}
@@ -167,7 +166,7 @@ public abstract class TextClassifierService extends Service {
public void onTextClassifierEvent(
TextClassificationSessionId sessionId,
TextClassifierEvent event) {
- Preconditions.checkNotNull(event);
+ Objects.requireNonNull(event);
mMainThreadHandler.post(
() -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
}
@@ -177,8 +176,8 @@ public abstract class TextClassifierService extends Service {
TextClassificationSessionId sessionId,
TextLanguage.Request request,
ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -188,8 +187,8 @@ public abstract class TextClassifierService extends Service {
TextClassificationSessionId sessionId,
ConversationActions.Request request,
ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -197,8 +196,8 @@ public abstract class TextClassifierService extends Service {
@Override
public void onCreateTextClassificationSession(
TextClassificationContext context, TextClassificationSessionId sessionId) {
- Preconditions.checkNotNull(context);
- Preconditions.checkNotNull(sessionId);
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(sessionId);
mMainThreadHandler.post(
() -> TextClassifierService.this.onCreateTextClassificationSession(
context, sessionId));
@@ -487,7 +486,7 @@ public abstract class TextClassifierService extends Service {
private ITextClassifierCallback mTextClassifierCallback;
private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
- mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
+ mTextClassifierCallback = Objects.requireNonNull(textClassifierCallback);
}
@Override
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index a2b22e8d8a7a..b516b0254f5c 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -114,9 +114,15 @@ import java.util.Objects;
*
* <p>Threading:
*
- * <p>Calls to {@code report} methods can be made on on any thread and will be passed asynchronously
- * to the system server. Calls to {@link #onStartUpdates(long)} and {@link #onStopUpdates()} will
- * occur on a single thread.
+ * <p>Outgoing calls to {@code report} methods can be made on any thread and will be delivered
+ * asynchronously to the system server. Incoming calls to {@link TimeZoneProviderService}-defined
+ * service methods like {@link #onStartUpdates(long)} and {@link #onStopUpdates()} are also
+ * asynchronous with respect to the system server caller and will be delivered to this service using
+ * a single thread. {@link Service} lifecycle method calls like {@link #onCreate()} and {@link
+ * #onDestroy()} can occur on a different thread from those made to {@link
+ * TimeZoneProviderService}-defined service methods, so implementations must be defensive and not
+ * assume an ordering between them, e.g. a call to {@link #onStopUpdates()} can occur after {@link
+ * #onDestroy()} and should be handled safely.
*
* @hide
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 725e20f2a74d..d46265e52d68 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -68,7 +68,6 @@ import com.android.internal.app.IVoiceInteractorCallback;
import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
@@ -77,6 +76,7 @@ import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -1363,9 +1363,9 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor resultExecutor,
@NonNull Consumer<List<DirectAction>> callback) {
- Preconditions.checkNotNull(activityId);
- Preconditions.checkNotNull(resultExecutor);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(activityId);
+ Objects.requireNonNull(resultExecutor);
+ Objects.requireNonNull(callback);
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
@@ -1444,8 +1444,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
- Preconditions.checkNotNull(resultExecutor);
- Preconditions.checkNotNull(resultListener);
+ Objects.requireNonNull(resultExecutor);
+ Objects.requireNonNull(resultListener);
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4b18c5aae614..bedad7344e9d 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -111,7 +111,12 @@ public class TelephonyRegistryManager {
IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
@Override
public void onSubscriptionsChanged () {
- executor.execute(() -> listener.onSubscriptionsChanged());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onSubscriptionsChanged());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
};
mSubscriptionChangedListenerMap.put(listener, callback);
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index 3287c279c87f..590ba1a8b589 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -157,4 +157,28 @@ public class SparseArrayMap<K, V> {
}
}
}
+
+ /**
+ * @param <K> Any class
+ * @param <V> Any class
+ * @hide
+ */
+ public interface TriConsumer<K, V> {
+ /** Consume the int-K-V tuple. */
+ void accept(int key, K mapKey, V value);
+ }
+
+ /**
+ * Iterate through all int-K pairs and operate on all of the values.
+ * @hide
+ */
+ public void forEach(@NonNull TriConsumer<K, V> consumer) {
+ for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) {
+ final int i = mData.keyAt(iIdx);
+ final ArrayMap<K, V> data = mData.valueAt(i);
+ for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) {
+ consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx));
+ }
+ }
+ }
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 73f7543ba819..0c04976becfe 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -23,10 +23,10 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFIC
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Trace;
@@ -65,7 +65,7 @@ public class ApkSignatureVerifier {
*
* @throws PackageParserException if the APK's signature failed to verify.
*/
- public static PackageParser.SigningDetails verify(String apkPath,
+ public static SigningDetails verify(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
return verifySignatures(apkPath, minSignatureSchemeVersion, true);
@@ -78,7 +78,7 @@ public class ApkSignatureVerifier {
*
* @throws PackageParserException if there was a problem collecting certificates.
*/
- public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
+ public static SigningDetails unsafeGetCertsWithoutVerification(
String apkPath, int minSignatureSchemeVersion)
throws PackageParserException {
return verifySignatures(apkPath, minSignatureSchemeVersion, false);
@@ -90,7 +90,7 @@ public class ApkSignatureVerifier {
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @throws PackageParserException if there was a problem collecting certificates
*/
- private static PackageParser.SigningDetails verifySignatures(String apkPath,
+ private static SigningDetails verifySignatures(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
throws PackageParserException {
return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
@@ -259,7 +259,7 @@ public class ApkSignatureVerifier {
}
}
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V4, pastSignerSigs),
vSigner.contentDigests);
} catch (SignatureNotFoundException e) {
@@ -301,7 +301,7 @@ public class ApkSignatureVerifier {
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
vSigner.contentDigests);
} catch (SignatureNotFoundException e) {
@@ -332,7 +332,7 @@ public class ApkSignatureVerifier {
ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
Certificate[][] signerCerts = vSigner.certs;
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
} catch (SignatureNotFoundException e) {
throw e;
@@ -419,7 +419,7 @@ public class ApkSignatureVerifier {
}
}
return new SigningDetailsWithDigests(
- new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
+ new SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
@@ -576,7 +576,7 @@ public class ApkSignatureVerifier {
* @hide for internal use only.
*/
public static class SigningDetailsWithDigests {
- public final PackageParser.SigningDetails signingDetails;
+ public final SigningDetails signingDetails;
/**
* APK Signature Schemes v2/v3/v4 might contain multiple content digests.
@@ -587,7 +587,7 @@ public class ApkSignatureVerifier {
*/
public final Map<Integer, byte[]> contentDigests;
- SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+ SigningDetailsWithDigests(SigningDetails signingDetails,
Map<Integer, byte[]> contentDigests) {
this.signingDetails = signingDetails;
this.contentDigests = contentDigests;
diff --git a/core/java/android/util/imetracing/OWNERS b/core/java/android/util/imetracing/OWNERS
deleted file mode 100644
index 885fd0ab9a45..000000000000
--- a/core/java/android/util/imetracing/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-set noparent
-
-include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 199ecb3e5df2..d23200bd3c80 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -616,9 +616,12 @@ public final class AccessibilityInteractionController {
// focus instead fetching all provider nodes to do the search here.
AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
if (provider != null) {
- if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
- focused = AccessibilityNodeInfo.obtain(
- mViewRootImpl.mAccessibilityFocusedVirtualView);
+ final AccessibilityNodeInfo focusNode =
+ mViewRootImpl.mAccessibilityFocusedVirtualView;
+ if (focusNode != null) {
+ final int virtualNodeId = AccessibilityNodeInfo
+ .getVirtualDescendantId(focusNode.getSourceNodeId());
+ focused = provider.createAccessibilityNodeInfo(virtualNodeId);
}
} else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
focused = host.createAccessibilityNodeInfo();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 802163617b3b..f4539c2f8859 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -814,9 +814,10 @@ interface IWindowManager
* @param displayId The display associated with the window context
* @param options A bundle used to pass window-related options and choose the right DisplayArea
*
- * @return {@code true} if the WindowContext is attached to the DisplayArea successfully.
+ * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
+ * attached to the DisplayArea successfully. {@code null}, otherwise.
*/
- boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
+ Configuration attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
in Bundle options);
/**
@@ -865,4 +866,11 @@ interface IWindowManager
void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
boolean isTaskSnapshotSupported();
+
+ /**
+ * Returns the preferred display ID to show software keyboard.
+ *
+ * @see android.window.WindowProviderService#getLaunchedDisplayId
+ */
+ int getImeDisplayId();
}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 5a34a92a4b1a..bd97ef0c43f6 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -16,12 +16,10 @@
package android.view;
-import static android.view.Display.INVALID_DISPLAY;
-
import android.annotation.Nullable;
import android.graphics.Region;
+import android.gui.TouchOcclusionMode;
import android.os.IBinder;
-import android.os.TouchOcclusionMode;
import java.lang.ref.WeakReference;
@@ -101,10 +99,6 @@ public final class InputWindowHandle {
// Display this input is on.
public int displayId;
- // If this value is set to a valid display ID, it indicates this window is a portal which
- // transports the touch of this window to the display indicated by portalToDisplayId.
- public int portalToDisplayId = INVALID_DISPLAY;
-
/**
* Crops the touchable region to the bounds of the surface provided.
*
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6f915c9182d2..1a9b34ec1b9f 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -45,7 +45,6 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
@@ -61,6 +60,7 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.inputmethod.ImeTracing;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ee33541d9f40..bf9de39124c9 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -36,13 +36,13 @@ import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.Log;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.ImeTracing;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 69ff64f3d6a5..40942ea7f551 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -4008,6 +4008,22 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public float orientation;
/**
+ * The movement of x position of a motion event.
+ *
+ * @see MotionEvent#AXIS_RELATIVE_X
+ * @hide
+ */
+ public float relativeX;
+
+ /**
+ * The movement of y position of a motion event.
+ *
+ * @see MotionEvent#AXIS_RELATIVE_Y
+ * @hide
+ */
+ public float relativeY;
+
+ /**
* Clears the contents of this object.
* Resets all axes to zero.
*/
@@ -4023,6 +4039,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
toolMajor = 0;
toolMinor = 0;
orientation = 0;
+ relativeX = 0;
+ relativeY = 0;
}
/**
@@ -4053,6 +4071,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
toolMajor = other.toolMajor;
toolMinor = other.toolMinor;
orientation = other.orientation;
+ relativeX = other.relativeX;
+ relativeY = other.relativeY;
}
/**
@@ -4084,6 +4104,10 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return toolMinor;
case AXIS_ORIENTATION:
return orientation;
+ case AXIS_RELATIVE_X:
+ return relativeX;
+ case AXIS_RELATIVE_Y:
+ return relativeY;
default: {
if (axis < 0 || axis > 63) {
throw new IllegalArgumentException("Axis out of range.");
@@ -4137,6 +4161,12 @@ public final class MotionEvent extends InputEvent implements Parcelable {
case AXIS_ORIENTATION:
orientation = value;
break;
+ case AXIS_RELATIVE_X:
+ relativeX = value;
+ break;
+ case AXIS_RELATIVE_Y:
+ relativeY = value;
+ break;
default: {
if (axis < 0 || axis > 63) {
throw new IllegalArgumentException("Axis out of range.");
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
index a78036fba094..e1cc60491f72 100644
--- a/core/java/android/view/RemoteAnimationAdapter.java
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -17,6 +17,7 @@
package android.view;
import android.app.ActivityOptions;
+import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -58,6 +59,9 @@ public class RemoteAnimationAdapter implements Parcelable {
private int mCallingPid;
private int mCallingUid;
+ /** @see #getCallingApplication */
+ private IApplicationThread mCallingApplication;
+
/**
* @param runner The interface that gets notified when we actually need to start the animation.
* @param duration The duration of the animation.
@@ -81,11 +85,19 @@ public class RemoteAnimationAdapter implements Parcelable {
this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
}
+ @UnsupportedAppUsage
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay, IApplicationThread callingApplication) {
+ this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
+ mCallingApplication = callingApplication;
+ }
+
public RemoteAnimationAdapter(Parcel in) {
mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
mDuration = in.readLong();
mStatusBarTransitionDelay = in.readLong();
mChangeNeedsSnapshot = in.readBoolean();
+ mCallingApplication = IApplicationThread.Stub.asInterface(in.readStrongBinder());
}
public IRemoteAnimationRunner getRunner() {
@@ -126,6 +138,15 @@ public class RemoteAnimationAdapter implements Parcelable {
return mCallingUid;
}
+ /**
+ * Gets the ApplicationThread that will run the animation. Instead it is intended to pass the
+ * calling information among client processes (eg. shell + launcher) through one-way binder
+ * calls (where binder itself doesn't track calling information).
+ */
+ public IApplicationThread getCallingApplication() {
+ return mCallingApplication;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -137,6 +158,7 @@ public class RemoteAnimationAdapter implements Parcelable {
dest.writeLong(mDuration);
dest.writeLong(mStatusBarTransitionDelay);
dest.writeBoolean(mChangeNeedsSnapshot);
+ dest.writeStrongInterface(mCallingApplication);
}
public static final @android.annotation.NonNull Creator<RemoteAnimationAdapter> CREATOR
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 4e2f37fde1f7..b300f3031a00 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -162,6 +162,8 @@ public final class SurfaceControl implements Parcelable {
IBinder displayToken, long nativeSurfaceObject);
private static native void nativeSetDisplayLayerStack(long transactionObj,
IBinder displayToken, int layerStack);
+ private static native void nativeSetDisplayFlags(long transactionObj,
+ IBinder displayToken, int flags);
private static native void nativeSetDisplayProjection(long transactionObj,
IBinder displayToken, int orientation,
int l, int t, int r, int b,
@@ -548,6 +550,15 @@ public final class SurfaceControl implements Parcelable {
*/
private static final int SURFACE_OPAQUE = 0x02;
+ /* flags used with setDisplayFlags() (keep in sync with DisplayDevice.h) */
+
+ /**
+ * DisplayDevice flag: This display's transform is sent to inputflinger and used for input
+ * dispatch. This flag is used to disambiguate displays which share a layerstack.
+ * @hide
+ */
+ public static final int DISPLAY_RECEIVES_INPUT = 0x01;
+
// Display power modes.
/**
* Display power mode off: used while blanking the screen.
@@ -3167,6 +3178,17 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
+ public Transaction setDisplayFlags(IBinder displayToken, int flags) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ nativeSetDisplayFlags(mNativeObject, displayToken, flags);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public Transaction setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4f2cf6d9001e..f04530f188e2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1559,12 +1559,21 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
+ setResizeBackgroundColor(mTmpTransaction, bgColor);
+ mTmpTransaction.apply();
+ }
+
+ /**
+ * Version of {@link #setResizeBackgroundColor(int)} that allows you to provide
+ * {@link SurfaceControl.Transaction}.
+ * @hide
+ */
+ public void setResizeBackgroundColor(@NonNull SurfaceControl.Transaction t, int bgColor) {
if (mBackgroundControl == null) {
return;
}
-
mBackgroundColor = bgColor;
- updateBackgroundColor(mTmpTransaction).apply();
+ updateBackgroundColor(t);
}
@UnsupportedAppUsage
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9450801d8d83..1793eaf2ec57 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6432,7 +6432,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@Override
public String toString() {
- StringBuilder out = new StringBuilder(128);
+ StringBuilder out = new StringBuilder(256);
out.append(getClass().getName());
out.append('{');
out.append(Integer.toHexString(System.identityHashCode(this)));
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 679da313edcc..488f7c3fca8b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2723,7 +2723,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
continue;
}
childWithAccessibilityFocus = null;
- i = childrenCount - 1;
+ i = childrenCount;
}
if (!child.canReceivePointerEvents()
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3550a31f9038..b9346b5dedf8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -151,7 +151,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
@@ -192,6 +191,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
@@ -253,11 +253,11 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
- * Whether or not to report end-to-end input latency. Disabled temporarily as a
+ * Whether or not to report end-to-end input latency. Can be disabled temporarily as a
* risk mitigation against potential jank caused by acquiring a weak reference
- * per frame
+ * per frame.
*/
- private static final boolean ENABLE_INPUT_LATENCY_TRACKING = false;
+ private static final boolean ENABLE_INPUT_LATENCY_TRACKING = true;
/**
* Set this system property to true to force the view hierarchy to render
@@ -291,6 +291,21 @@ public final class ViewRootImpl implements ViewParent,
*/
private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500;
+ /**
+ * If set to {@code true}, the new logic to layout system bars as normal window and to use
+ * layout result to get insets will be applied. Otherwise, the old hard-coded window logic will
+ * be applied.
+ */
+ private static final String USE_FLEXIBLE_INSETS = "persist.debug.flexible_insets";
+
+ /**
+ * A flag to indicate to use the new generalized insets window logic, or the old hard-coded
+ * insets window layout logic.
+ * {@hide}
+ */
+ public static final boolean INSETS_LAYOUT_GENERALIZATION =
+ SystemProperties.getBoolean(USE_FLEXIBLE_INSETS, false);
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -388,6 +403,8 @@ public final class ViewRootImpl implements ViewParent,
View mView;
View mAccessibilityFocusedHost;
+ // Accessibility-focused virtual view. The bounds and sourceNodeId of
+ // mAccessibilityFocusedVirtualView is up-to-date while other fields may be stale.
AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
// True if the window currently has pointer capture enabled.
@@ -4813,6 +4830,9 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Get accessibility-focused virtual view. The bounds and sourceNodeId of the returned node is
+ * up-to-date while other fields may be stale.
+ *
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 55beae0f7b3d..fcfb0ab1a744 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -97,6 +97,7 @@ import android.content.ClipData;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -394,6 +395,11 @@ public interface WindowManager extends ViewManager {
*/
int TRANSIT_KEYGUARD_UNOCCLUDE = 9;
/**
+ * A window is starting to enter PiP.
+ * @hide
+ */
+ int TRANSIT_PIP = 10;
+ /**
* The first slot for custom transition types. Callers (like Shell) can make use of custom
* transition types for dealing with special cases. These types are effectively ignored by
* Core and will just be passed along as part of TransitionInfo objects. An example is
@@ -402,7 +408,7 @@ public interface WindowManager extends ViewManager {
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 10;
+ int TRANSIT_FIRST_CUSTOM = 11;
/**
* @hide
@@ -418,6 +424,7 @@ public interface WindowManager extends ViewManager {
TRANSIT_KEYGUARD_GOING_AWAY,
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_KEYGUARD_UNOCCLUDE,
+ TRANSIT_PIP,
TRANSIT_FIRST_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@@ -467,6 +474,13 @@ public interface WindowManager extends ViewManager {
int TRANSIT_FLAG_KEYGUARD_LOCKED = 0x40;
/**
+ * Transition flag: Indicates that this transition is for recents animation.
+ * TODO(b/188669821): Remove once special-case logic moves to shell.
+ * @hide
+ */
+ int TRANSIT_FLAG_IS_RECENTS = 0x80;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -476,7 +490,8 @@ public interface WindowManager extends ViewManager {
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
TRANSIT_FLAG_APP_CRASHED,
TRANSIT_FLAG_OPEN_BEHIND,
- TRANSIT_FLAG_KEYGUARD_LOCKED
+ TRANSIT_FLAG_KEYGUARD_LOCKED,
+ TRANSIT_FLAG_IS_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
@@ -3468,6 +3483,22 @@ public interface WindowManager extends ViewManager {
public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
/**
+ * If specified, the insets provided by this window will be our window frame minus the
+ * insets specified by providedInternalInsets.
+ *
+ * @hide
+ */
+ public Insets providedInternalInsets = Insets.NONE;
+
+ /**
+ * {@link LayoutParams} to be applied to the window when layout with a assigned rotation.
+ * This will make layout during rotation change smoothly.
+ *
+ * @hide
+ */
+ public LayoutParams[] paramsForRotation;
+
+ /**
* Specifies types of insets that this window should avoid overlapping during layout.
*
* @param types which {@link WindowInsets.Type}s of insets that this window should avoid.
@@ -3566,6 +3597,18 @@ public interface WindowManager extends ViewManager {
return mFitInsetsIgnoringVisibility;
}
+ private void checkNonRecursiveParams() {
+ if (paramsForRotation == null) {
+ return;
+ }
+ for (int i = paramsForRotation.length - 1; i >= 0; i--) {
+ if (paramsForRotation[i].paramsForRotation != null) {
+ throw new IllegalArgumentException(
+ "Params cannot contain params recursively.");
+ }
+ }
+ }
+
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
@@ -3820,6 +3863,14 @@ public interface WindowManager extends ViewManager {
} else {
out.writeInt(0);
}
+ providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */);
+ if (paramsForRotation != null) {
+ checkNonRecursiveParams();
+ out.writeInt(paramsForRotation.length);
+ out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
+ } else {
+ out.writeInt(0);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -3891,6 +3942,12 @@ public interface WindowManager extends ViewManager {
providesInsetsTypes = new int[insetsTypesLength];
in.readIntArray(providesInsetsTypes);
}
+ providedInternalInsets = Insets.CREATOR.createFromParcel(in);
+ int paramsForRotationLength = in.readInt();
+ if (paramsForRotationLength > 0) {
+ paramsForRotation = new LayoutParams[paramsForRotationLength];
+ in.readTypedArray(paramsForRotation, LayoutParams.CREATOR);
+ }
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -4187,6 +4244,17 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
+ if (!providedInternalInsets.equals(o.providedInternalInsets)) {
+ providedInternalInsets = o.providedInternalInsets;
+ changes |= LAYOUT_CHANGED;
+ }
+
+ if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) {
+ paramsForRotation = o.paramsForRotation;
+ checkNonRecursiveParams();
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -4382,6 +4450,18 @@ public interface WindowManager extends ViewManager {
sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
}
}
+ if (!providedInternalInsets.equals(Insets.NONE)) {
+ sb.append(" providedInternalInsets=");
+ sb.append(providedInternalInsets);
+ }
+ if (paramsForRotation != null && paramsForRotation.length != 0) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" paramsForRotation=");
+ for (int i = 0; i < paramsForRotation.length; ++i) {
+ if (i > 0) sb.append(' ');
+ sb.append(paramsForRotation[i].toString());
+ }
+ }
sb.append('}');
return sb.toString();
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f6d6fde6435f..40da86a2ee43 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1298,6 +1298,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.readList(record.mText, null);
record.mSourceWindowId = parcel.readInt();
record.mSourceNodeId = parcel.readLong();
+ record.mSourceDisplayId = parcel.readInt();
record.mSealed = (parcel.readInt() == 1);
}
@@ -1364,6 +1365,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.writeList(record.mText);
parcel.writeInt(record.mSourceWindowId);
parcel.writeLong(record.mSourceNodeId);
+ parcel.writeInt(record.mSourceDisplayId);
parcel.writeInt(record.mSealed ? 1 : 0);
}
@@ -1402,6 +1404,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
if (DEBUG) {
builder.append("; SourceWindowId: 0x").append(Long.toHexString(mSourceWindowId));
builder.append("; SourceNodeId: 0x").append(Long.toHexString(mSourceNodeId));
+ builder.append("; SourceDisplayId: ").append(mSourceDisplayId);
}
for (int i = 0; i < getRecordCount(); i++) {
builder.append(" Record ").append(i).append(":");
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index dd81dd93380b..aac09b8a3b57 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -16,6 +16,9 @@
package android.view.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -86,6 +89,7 @@ public final class AccessibilityInteractionClient
public static final int NO_ID = -1;
public static final String CALL_STACK = "call_stack";
+ public static final String IGNORE_CALL_STACK = "ignore_call_stack";
private static final String LOG_TAG = "AccessibilityInteractionClient";
@@ -121,6 +125,12 @@ public final class AccessibilityInteractionClient
private volatile int mInteractionId = -1;
private volatile int mCallingUid = Process.INVALID_UID;
+ // call stack for IAccessibilityInteractionConnectionCallback APIs. These callback APIs are
+ // shared by multiple requests APIs in IAccessibilityServiceConnection. To correctly log the
+ // request API which triggers the callback, we log trace entries for callback after the
+ // request API thread waiting for the callback returns. To log the correct callback stack in
+ // the request API thread, we save the callback stack in this member variables.
+ private List<StackTraceElement> mCallStackOfCallback;
private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
@@ -307,18 +317,30 @@ public final class AccessibilityInteractionClient
if (DEBUG) {
Log.i(LOG_TAG, "Window cache hit");
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";bypassCache=false");
+ }
return window;
}
if (DEBUG) {
Log.i(LOG_TAG, "Window cache miss");
}
}
+
final long identityToken = Binder.clearCallingIdentity();
try {
window = connection.getWindow(accessibilityWindowId);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow", "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId + ";bypassCache="
+ + bypassCache);
+ }
+
if (window != null) {
if (!bypassCache) {
sAccessibilityCache.addWindow(window);
@@ -368,6 +390,10 @@ public final class AccessibilityInteractionClient
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
+ if (shouldTraceClient()) {
+ logTraceClient(
+ connection, "getWindows cache", "connectionId=" + connectionId);
+ }
return windows;
}
if (DEBUG) {
@@ -379,6 +405,9 @@ public final class AccessibilityInteractionClient
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
+ }
if (windows != null) {
sAccessibilityCache.setWindowsOnAllDisplays(windows);
return windows;
@@ -472,6 +501,15 @@ public final class AccessibilityInteractionClient
Log.i(LOG_TAG, "Node cache hit for "
+ idToString(accessibilityWindowId, accessibilityNodeId));
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection,
+ "findAccessibilityNodeInfoByAccessibilityId cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";bypassCache=" + bypassCache
+ + ";prefetchFlags=" + prefetchFlags + ";arguments="
+ + arguments);
+ }
return cachedInfo;
}
if (DEBUG) {
@@ -488,6 +526,14 @@ public final class AccessibilityInteractionClient
prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfoByAccessibilityId",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";bypassCache="
+ + bypassCache + ";prefetchFlags=" + prefetchFlags + ";arguments="
+ + arguments);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -500,16 +546,10 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
AccessibilityNodeInfo info =
getFindAccessibilityNodeInfoResultAndClear(interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId",
- "InteractionId:" + interactionId + ";Result: " + info
- + ";connectionId=" + connectionId
- + ";accessibilityWindowId="
- + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";bypassCache=" + bypassCache
- + ";prefetchFlags=" + prefetchFlags
- + ";arguments=" + arguments);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfoByAccessibilityId",
+ "InteractionId:" + interactionId + ";connectionId="
+ + connectionId + ";Result: " + info);
}
if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0
&& info != null) {
@@ -571,6 +611,14 @@ public final class AccessibilityInteractionClient
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfosByViewId",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
+ + viewId);
+ }
+
packageNames = connection.findAccessibilityNodeInfosByViewId(
accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
Thread.currentThread().getId());
@@ -581,13 +629,10 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId="
- + interactionId + ":Result: " + infos + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
- + viewId);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfosByViewId",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ":Result: " + infos);
}
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -630,6 +675,12 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfosByText",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -643,12 +694,10 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId="
- + interactionId + ":Result: " + infos + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfosByText",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";Result: " + infos);
}
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -690,6 +739,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findFocus",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
+ + focusType);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -703,13 +759,9 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findFocus", "InteractionId=" + interactionId
- + ":Result: " + info + ";connectionId=" + connectionId
- + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
- + focusType);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findFocus", "InteractionId=" + interactionId
+ + ";connectionId=" + connectionId + ";Result:" + info);
}
finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
return info;
@@ -747,6 +799,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "focusSearch",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
+ + direction);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -761,13 +820,9 @@ public final class AccessibilityInteractionClient
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "focusSearch", "InteractionId=" + interactionId
- + ":Result: " + info + ";connectionId=" + connectionId
- + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
- + direction);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "focusSearch", "InteractionId=" + interactionId
+ + ";connectionId=" + connectionId + ";Result:" + info);
}
return info;
}
@@ -803,6 +858,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "performAccessibilityAction",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + action
+ + ";arguments=" + arguments);
+ }
final boolean success;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -816,13 +878,10 @@ public final class AccessibilityInteractionClient
if (success) {
final boolean result =
getPerformAccessibilityActionResultAndClear(interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "performAccessibilityAction", "InteractionId="
- + interactionId + ":Result: " + result + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";action="
- + action + ";arguments=" + arguments);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "performAccessibilityAction",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";Result: " + result);
}
return result;
}
@@ -886,6 +945,8 @@ public final class AccessibilityInteractionClient
mFindAccessibilityNodeInfoResult = info;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -936,6 +997,8 @@ public final class AccessibilityInteractionClient
}
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -975,13 +1038,15 @@ public final class AccessibilityInteractionClient
finalizeAndCacheAccessibilityNodeInfos(
infos, connectionIdWaitingForPrefetchResultCopy, false,
packageNamesForNextPrefetchResultCopy);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
+ if (shouldTraceCallback()) {
logTrace(getConnection(connectionIdWaitingForPrefetchResultCopy),
"setPrefetchAccessibilityNodeInfoResult",
- "InteractionId:" + interactionId + ";Result: " + infos
- + ";connectionId=" + connectionIdWaitingForPrefetchResultCopy,
- Binder.getCallingUid());
+ "InteractionId:" + interactionId + ";connectionId="
+ + connectionIdWaitingForPrefetchResultCopy + ";Result: " + infos,
+ Binder.getCallingUid(),
+ Arrays.asList(Thread.currentThread().getStackTrace()),
+ new HashSet<String>(Arrays.asList("getStackTrace")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
}
} else if (DEBUG) {
Log.w(LOG_TAG, "Prefetching for interaction with id " + interactionId + " dropped "
@@ -1013,6 +1078,8 @@ public final class AccessibilityInteractionClient
mPerformAccessibilityActionResult = succeeded;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -1222,24 +1289,45 @@ public final class AccessibilityInteractionClient
return true;
}
+ private boolean shouldTraceClient() {
+ return (mAccessibilityManager != null)
+ && mAccessibilityManager.isA11yInteractionClientTraceEnabled();
+ }
+
+ private boolean shouldTraceCallback() {
+ return (mAccessibilityManager != null)
+ && mAccessibilityManager.isA11yInteractionConnectionCBTraceEnabled();
+ }
+
private void logTrace(
IAccessibilityServiceConnection connection, String method, String params,
- int callingUid) {
+ int callingUid, List<StackTraceElement> callStack, HashSet<String> ignoreSet,
+ long logTypes) {
try {
Bundle b = new Bundle();
- ArrayList<StackTraceElement> callStack = new ArrayList<StackTraceElement>(
- Arrays.asList(Thread.currentThread().getStackTrace()));
- b.putSerializable(CALL_STACK, callStack);
+ b.putSerializable(CALL_STACK, new ArrayList<StackTraceElement>(callStack));
+ if (ignoreSet != null) {
+ b.putSerializable(IGNORE_CALL_STACK, ignoreSet);
+ }
connection.logTrace(SystemClock.elapsedRealtimeNanos(),
- LOG_TAG + ".callback for " + method, params, Process.myPid(),
- Thread.currentThread().getId(), callingUid, b);
+ LOG_TAG + "." + method,
+ logTypes, params, Process.myPid(), Thread.currentThread().getId(),
+ callingUid, b);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to log trace. " + e);
}
}
- private void logTrace(
+ private void logTraceCallback(
+ IAccessibilityServiceConnection connection, String method, String params) {
+ logTrace(connection, method + " callback", params, mCallingUid, mCallStackOfCallback,
+ new HashSet<String>(Arrays.asList("getStackTrace")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
+ }
+
+ private void logTraceClient(
IAccessibilityServiceConnection connection, String method, String params) {
- logTrace(connection, method, params, mCallingUid);
+ logTrace(connection, method, params, Binder.getCallingUid(),
+ Collections.emptyList(), null, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index f9cdbd322c26..17fad7e57de7 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -111,7 +111,13 @@ public final class AccessibilityManager {
public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010;
/** @hide */
- public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020;
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 0x00000100;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 0x00000200;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800;
/** @hide */
public static final int DALTONIZER_DISABLED = -1;
@@ -235,8 +241,8 @@ public final class AccessibilityManager {
@UnsupportedAppUsage(trackingBug = 123768939L)
boolean mIsHighTextContrastEnabled;
- // Whether accessibility tracing is enabled or not
- boolean mIsAccessibilityTracingEnabled = false;
+ // accessibility tracing state
+ int mAccessibilityTracingState = 0;
AccessibilityPolicy mAccessibilityPolicy;
@@ -1029,13 +1035,50 @@ public final class AccessibilityManager {
}
/**
- * Gets accessibility tracing enabled state.
+ * Gets accessibility interaction connection tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionConnectionTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility interaction connection callback tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionConnectionCBTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility interaction client tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionClientTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility service tracing enabled state.
*
* @hide
*/
- public boolean isAccessibilityTracingEnabled() {
+ public boolean isA11yServiceTraceEnabled() {
synchronized (mLock) {
- return mIsAccessibilityTracingEnabled;
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0);
}
}
@@ -1233,8 +1276,6 @@ public final class AccessibilityManager {
(stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
final boolean highTextContrastEnabled =
(stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
- final boolean accessibilityTracingEnabled =
- (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0;
final boolean wasEnabled = isEnabled();
final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
@@ -1257,7 +1298,7 @@ public final class AccessibilityManager {
notifyHighTextContrastStateChanged();
}
- updateAccessibilityTracingState(accessibilityTracingEnabled);
+ updateAccessibilityTracingState(stateFlags);
}
/**
@@ -1715,11 +1756,11 @@ public final class AccessibilityManager {
}
/**
- * Update mIsAccessibilityTracingEnabled.
+ * Update mAccessibilityTracingState.
*/
- private void updateAccessibilityTracingState(boolean enabled) {
+ private void updateAccessibilityTracingState(int stateFlag) {
synchronized (mLock) {
- mIsAccessibilityTracingEnabled = enabled;
+ mAccessibilityTracingState = stateFlag;
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index c3a4d3206462..f26abb2b46ef 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -20,8 +20,10 @@ import static com.android.internal.util.CollectionUtils.isEmpty;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcelable;
+import android.view.Display;
import android.view.View;
import java.util.ArrayList;
@@ -104,6 +106,7 @@ public class AccessibilityRecord {
@UnsupportedAppUsage
long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ int mSourceDisplayId = Display.INVALID_DISPLAY;
CharSequence mClassName;
CharSequence mContentDescription;
@@ -202,6 +205,27 @@ public class AccessibilityRecord {
}
/**
+ * Sets the display id.
+ *
+ * @param displayId The displayId id.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setDisplayId(int displayId) {
+ mSourceDisplayId = displayId;
+ }
+
+ /**
+ * Gets the id of the display from which the event comes from.
+ *
+ * @return The display id.
+ */
+ public int getDisplayId() {
+ return mSourceDisplayId;
+ }
+
+ /**
* Sets the window id.
*
* @param windowId The window id.
@@ -886,6 +910,7 @@ public class AccessibilityRecord {
mText.addAll(record.mText);
mSourceWindowId = record.mSourceWindowId;
mSourceNodeId = record.mSourceNodeId;
+ mSourceDisplayId = record.mSourceDisplayId;
mConnectionId = record.mConnectionId;
}
@@ -914,6 +939,7 @@ public class AccessibilityRecord {
mText.clear();
mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ mSourceDisplayId = Display.INVALID_DISPLAY;
mConnectionId = UNDEFINED;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d0651472bd16..11220561b00c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -25,6 +25,7 @@ import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -45,16 +46,21 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.IFillCallback;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -74,6 +80,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@@ -83,7 +90,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.SyncResultReceiver;
import org.xmlpull.v1.XmlPullParserException;
@@ -99,6 +105,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import sun.misc.Cleaner;
@@ -167,6 +174,12 @@ import sun.misc.Cleaner;
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
+ * <p>There is another choice for the application to provide it's datasets to the Autofill framework
+ * by setting an {@link AutofillRequestCallback} through
+ * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
+ * its callback instead of the default {@link AutofillService}. See
+ * {@link AutofillRequestCallback} for more details.
+ *
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -292,6 +305,7 @@ public final class AutofillManager {
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
+ /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -592,6 +606,11 @@ public final class AutofillManager {
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
+ @GuardedBy("mLock")
+ @Nullable private AutofillRequestCallback mAutofillRequestCallback;
+ @GuardedBy("mLock")
+ @Nullable private Executor mRequestCallbackExecutor;
+
/** @hide */
public interface AutofillClient {
/**
@@ -726,7 +745,7 @@ public final class AutofillManager {
* @hide
*/
public AutofillManager(Context context, IAutoFillManager service) {
- mContext = Preconditions.checkNotNull(context, "context cannot be null");
+ mContext = Objects.requireNonNull(context, "context cannot be null");
mService = service;
mOptions = context.getAutofillOptions();
@@ -1836,6 +1855,32 @@ public final class AutofillManager {
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
+ /**
+ * Sets the client's suggestions callback for autofill.
+ *
+ * @see AutofillRequestCallback
+ *
+ * @param executor specifies the thread upon which the callbacks will be invoked.
+ * @param callback which handles autofill request to provide client's suggestions.
+ */
+ public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AutofillRequestCallback callback) {
+ synchronized (mLock) {
+ mRequestCallbackExecutor = executor;
+ mAutofillRequestCallback = callback;
+ }
+ }
+
+ /**
+ * clears the client's suggestions callback for autofill.
+ */
+ public void clearAutofillRequestCallback() {
+ synchronized (mLock) {
+ mRequestCallbackExecutor = null;
+ mAutofillRequestCallback = null;
+ }
+ }
+
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -1896,6 +1941,13 @@ public final class AutofillManager {
}
}
+ if (mAutofillRequestCallback != null) {
+ if (sDebug) {
+ Log.d(TAG, "startSession with the client suggestions provider");
+ }
+ flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
+ }
+
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, clientActivity,
@@ -2245,6 +2297,28 @@ public final class AutofillManager {
}
}
+ private void onFillRequest(InlineSuggestionsRequest request,
+ CancellationSignal cancellationSignal, FillCallback callback) {
+ final AutofillRequestCallback autofillRequestCallback;
+ final Executor executor;
+ synchronized (mLock) {
+ autofillRequestCallback = mAutofillRequestCallback;
+ executor = mRequestCallbackExecutor;
+ }
+ if (autofillRequestCallback != null && executor != null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() ->
+ autofillRequestCallback.onFillRequest(
+ request, cancellationSignal, callback));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ callback.onSuccess(null);
+ }
+ }
+
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -3627,6 +3701,23 @@ public final class AutofillManager {
afm.post(() -> afm.requestShowSoftInput(id));
}
}
+
+ @Override
+ public void requestFillFromClient(int id, InlineSuggestionsRequest request,
+ IFillCallback callback) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ ICancellationSignal transport = CancellationSignal.createTransport();
+ try {
+ callback.onCancellable(transport);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error requesting a cancellation", e);
+ }
+
+ afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
+ new FillCallback(callback, id));
+ }
+ }
}
private static final class AugmentedAutofillManagerClient
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
new file mode 100644
index 000000000000..e632a5849471
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillRequestCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CancellationSignal;
+import android.service.autofill.FillCallback;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * <p>This class is used to provide some input suggestions to the Autofill framework.
+ *
+ * <P>When the user is requested to input something, Autofill will try to query input suggestions
+ * for the user choosing. If the application want to provide some internal input suggestions,
+ * implements this callback and register via
+ * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
+ * AutofillRequestCallback)}. Autofill will callback the
+ * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
+ * input suggestions.
+ *
+ * <P>To make sure the callback to take effect, must register before the autofill session starts.
+ * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
+ * session, and then the callback will be used at the next restarted session.
+ *
+ * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
+ * {@link AutofillId}s from its view structure. Below is an example:
+ * <pre class="prettyprint">
+ * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
+ * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
+ * </pre>
+ * To learn more about creating a {@link android.service.autofill.FillResponse}, read
+ * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
+ *
+ * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
+ * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
+ * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
+ * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
+ * client would like to keep no suggestions for the field, respond with an empty
+ * {@link android.service.autofill.FillResponse} which has no dataset.
+ *
+ * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
+ * the keyboard may choose to block your app from the inline strip.
+ */
+public interface AutofillRequestCallback {
+ /**
+ * Called by the Android system to decide if a screen can be autofilled by the callback.
+ *
+ * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
+ * currently inline suggestions are supported and can be displayed.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param callback object used to notify the result of the request.
+ */
+ void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 1f833f66c257..64507aac54cb 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,9 +24,11 @@ import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
+import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -140,4 +142,10 @@ oneway interface IAutoFillManagerClient {
* Requests to show the soft input method if the focus is on the given id.
*/
void requestShowSoftInput(in AutofillId id);
+
+ /**
+ * Requests to determine if a screen can be autofilled by the client app.
+ */
+ void requestFillFromClient(int id, in InlineSuggestionsRequest request,
+ in IFillCallback callback);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 1adef94255e0..027c8d20ccc6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -23,10 +23,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.DebugUtils;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Defines a condition for when content capture should be allowed.
@@ -61,7 +60,7 @@ public final class ContentCaptureCondition implements Parcelable {
* the {@code LocusId} used in the {@link ContentCaptureContext}).
*/
public ContentCaptureCondition(@NonNull LocusId locusId, @Flags int flags) {
- this.mLocusId = Preconditions.checkNotNull(locusId);
+ this.mLocusId = Objects.requireNonNull(locusId);
this.mFlags = flags;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 9998fbc02d12..71b8003954e6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -276,7 +276,7 @@ public final class ContentCaptureContext implements Parcelable {
* @param id id associated with this context.
*/
public Builder(@NonNull LocusId id) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
}
/**
@@ -291,7 +291,7 @@ public final class ContentCaptureContext implements Parcelable {
*/
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
- mExtras = Preconditions.checkNotNull(extras);
+ mExtras = Objects.requireNonNull(extras);
throwIfDestroyed();
return this;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ce6d034c585e..ae45c6e034e1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -33,13 +33,12 @@ import android.util.Log;
import android.view.autofill.AutofillId;
import android.view.inputmethod.BaseInputConnection;
-import com.android.internal.util.Preconditions;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/** @hide */
@SystemApi
@@ -173,13 +172,13 @@ public final class ContentCaptureEvent implements Parcelable {
/** @hide */
public ContentCaptureEvent setAutofillId(@NonNull AutofillId id) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
return this;
}
/** @hide */
public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
- mIds = Preconditions.checkNotNull(ids);
+ mIds = Objects.requireNonNull(ids);
return this;
}
@@ -189,7 +188,7 @@ public final class ContentCaptureEvent implements Parcelable {
* @hide
*/
public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) {
- Preconditions.checkNotNull(id);
+ Objects.requireNonNull(id);
if (mIds == null) {
mIds = new ArrayList<>();
if (mId == null) {
@@ -253,7 +252,7 @@ public final class ContentCaptureEvent implements Parcelable {
/** @hide */
@NonNull
public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
- mNode = Preconditions.checkNotNull(node);
+ mNode = Objects.requireNonNull(node);
return this;
}
@@ -425,7 +424,7 @@ public final class ContentCaptureEvent implements Parcelable {
* @hide
*/
public void mergeEvent(@NonNull ContentCaptureEvent event) {
- Preconditions.checkNotNull(event);
+ Objects.requireNonNull(event);
final int eventType = event.getType();
if (mType != eventType) {
Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType) + ") cannot be merged "
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9241c3074ddd..8514f6fb4ccf 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -49,7 +49,6 @@ import android.view.WindowManager;
import android.view.contentcapture.ContentCaptureSession.FlushReason;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.SyncResultReceiver;
import java.io.PrintWriter;
@@ -59,6 +58,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -398,9 +398,9 @@ public final class ContentCaptureManager {
/** @hide */
public ContentCaptureManager(@NonNull Context context,
@NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) {
- mContext = Preconditions.checkNotNull(context, "context cannot be null");
- mService = Preconditions.checkNotNull(service, "service cannot be null");
- mOptions = Preconditions.checkNotNull(options, "options cannot be null");
+ mContext = Objects.requireNonNull(context, "context cannot be null");
+ mService = Objects.requireNonNull(service, "service cannot be null");
+ mOptions = Objects.requireNonNull(options, "options cannot be null");
ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel);
@@ -679,7 +679,7 @@ public final class ContentCaptureManager {
* @param request object specifying what user data should be removed.
*/
public void removeData(@NonNull DataRemovalRequest request) {
- Preconditions.checkNotNull(request);
+ Objects.requireNonNull(request);
try {
mService.removeData(request);
@@ -703,9 +703,9 @@ public final class ContentCaptureManager {
public void shareData(@NonNull DataShareRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull DataShareWriteAdapter dataShareWriteAdapter) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(dataShareWriteAdapter);
- Preconditions.checkNotNull(executor);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(dataShareWriteAdapter);
+ Objects.requireNonNull(executor);
try {
mService.shareData(request,
@@ -840,9 +840,9 @@ public final class ContentCaptureManager {
private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter,
LocalDataShareAdapterResourceManager resourceManager) {
- Preconditions.checkNotNull(executor);
- Preconditions.checkNotNull(adapter);
- Preconditions.checkNotNull(resourceManager);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(resourceManager);
resourceManager.initializeForDelegate(this, adapter, executor);
mResourceManagerReference = new WeakReference<>(resourceManager);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index cc47f09d4e8d..955269160e49 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -41,6 +41,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Session used when notifying the Android system about events associated with views.
@@ -231,7 +232,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
// Used by ChildCOntentCaptureSession
ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
this();
- mClientContext = Preconditions.checkNotNull(initialContext);
+ mClientContext = Objects.requireNonNull(initialContext);
}
/** @hide */
@@ -362,7 +363,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @param node node that has been added.
*/
public final void notifyViewAppeared(@NonNull ViewStructure node) {
- Preconditions.checkNotNull(node);
+ Objects.requireNonNull(node);
if (!isContentCaptureEnabled()) return;
if (!(node instanceof ViewNode.ViewStructureImpl)) {
@@ -383,7 +384,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @param id id of the node that has been removed.
*/
public final void notifyViewDisappeared(@NonNull AutofillId id) {
- Preconditions.checkNotNull(id);
+ Objects.requireNonNull(id);
if (!isContentCaptureEnabled()) return;
internalNotifyViewDisappeared(id);
@@ -424,7 +425,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @param text new text.
*/
public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
- Preconditions.checkNotNull(id);
+ Objects.requireNonNull(id);
if (!isContentCaptureEnabled()) return;
@@ -438,7 +439,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* Notifies the Intelligence Service that the insets of a view have changed.
*/
public final void notifyViewInsetsChanged(@NonNull Insets viewInsets) {
- Preconditions.checkNotNull(viewInsets);
+ Objects.requireNonNull(viewInsets);
if (!isContentCaptureEnabled()) return;
@@ -534,7 +535,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @throws IllegalArgumentException if the {@code parentId} is a virtual child id.
*/
public @NonNull AutofillId newAutofillId(@NonNull AutofillId hostId, long virtualChildId) {
- Preconditions.checkNotNull(hostId);
+ Objects.requireNonNull(hostId);
Preconditions.checkArgument(hostId.isNonVirtual(), "hostId cannot be virtual: %s", hostId);
return new AutofillId(hostId, virtualChildId, mId);
}
diff --git a/core/java/android/view/contentcapture/DataRemovalRequest.java b/core/java/android/view/contentcapture/DataRemovalRequest.java
index f403dacee13e..3d861137d670 100644
--- a/core/java/android/view/contentcapture/DataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/DataRemovalRequest.java
@@ -29,6 +29,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Class used by apps to request the content capture service to remove data associated with
@@ -143,7 +144,7 @@ public final class DataRemovalRequest implements Parcelable {
public Builder addLocusId(@NonNull LocusId locusId, @Flags int flags) {
throwIfDestroyed();
Preconditions.checkState(!mForEverything, "Already is for everything");
- Preconditions.checkNotNull(locusId);
+ Objects.requireNonNull(locusId);
// felipeal: check flags
if (mLocusIds == null) {
diff --git a/core/java/android/view/contentcapture/DataShareRequest.java b/core/java/android/view/contentcapture/DataShareRequest.java
index 78c0ef9568ba..b2eabf113777 100644
--- a/core/java/android/view/contentcapture/DataShareRequest.java
+++ b/core/java/android/view/contentcapture/DataShareRequest.java
@@ -24,7 +24,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
-import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
/** Container class representing a request to share data with Content Capture service. */
@DataClass(
@@ -47,7 +48,7 @@ public final class DataShareRequest implements Parcelable {
/** Constructs a request to share data with the Content Capture Service. */
public DataShareRequest(@Nullable LocusId locusId, @NonNull String mimeType) {
- Preconditions.checkNotNull(mimeType);
+ Objects.requireNonNull(mimeType);
mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName();
mLocusId = locusId;
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index c882c6e3200d..1b4a00f81e44 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -34,7 +34,7 @@ import android.view.ViewStructure.HtmlInfo.Builder;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
//TODO(b/122484602): add javadocs / implement Parcelable / implement
//TODO(b/122484602): for now it's extending ViewNode directly as it needs most of its properties,
@@ -659,7 +659,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
/** @hide */
@TestApi
public ViewStructureImpl(@NonNull View view) {
- mNode.mAutofillId = Preconditions.checkNotNull(view).getAutofillId();
+ mNode.mAutofillId = Objects.requireNonNull(view).getAutofillId();
final ViewParent parent = view.getParent();
if (parent instanceof View) {
mNode.mParentAutofillId = ((View) parent).getAutofillId();
@@ -669,7 +669,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
/** @hide */
@TestApi
public ViewStructureImpl(@NonNull AutofillId parentId, long virtualId, int sessionId) {
- mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+ mNode.mParentAutofillId = Objects.requireNonNull(parentId);
mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
}
@@ -830,7 +830,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
@Override
public void setTextIdEntry(@NonNull String entryName) {
- mNode.mTextIdEntry = Preconditions.checkNotNull(entryName);
+ mNode.mTextIdEntry = Objects.requireNonNull(entryName);
}
@Override
@@ -840,7 +840,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
@Override
public void setHintIdEntry(String entryName) {
- mNode.mHintIdEntry = Preconditions.checkNotNull(entryName);
+ mNode.mHintIdEntry = Objects.requireNonNull(entryName);
}
@Override
@@ -913,13 +913,13 @@ public final class ViewNode extends AssistStructure.ViewNode {
@Override
public void setAutofillId(AutofillId id) {
- mNode.mAutofillId = Preconditions.checkNotNull(id);
+ mNode.mAutofillId = Objects.requireNonNull(id);
}
@Override
public void setAutofillId(AutofillId parentId, int virtualId) {
- mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+ mNode.mParentAutofillId = Objects.requireNonNull(parentId);
mNode.mAutofillId = new AutofillId(parentId, virtualId);
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 1eb1a9345d94..e1e175512edc 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,6 +111,22 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
/**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mServiceSupported;
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mClientSupported;
+
+ /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -204,6 +220,14 @@ public final class InlineSuggestionsRequest implements Parcelable {
return Bundle.EMPTY;
}
+ private static boolean defaultServiceSupported() {
+ return true;
+ }
+
+ private static boolean defaultClientSupported() {
+ return true;
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setInlinePresentationSpecs(
@@ -216,15 +240,25 @@ public final class InlineSuggestionsRequest implements Parcelable {
abstract Builder setHostDisplayId(int value);
}
+ /** @hide */
+ public boolean isServiceSupported() {
+ return mServiceSupported;
+ }
+
+ /** @hide */
+ public boolean isClientSupported() {
+ return mClientSupported;
+ }
+
- // Code below generated by codegen v1.0.23.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -240,7 +274,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
@NonNull Bundle extras,
@Nullable IBinder hostInputToken,
int hostDisplayId,
- @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
+ @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
+ boolean serviceSupported,
+ boolean clientSupported) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -257,6 +293,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -340,7 +378,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
/**
- * Specifies the UI specification for the inline suggestion tooltip in the response.
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
*/
@DataClass.Generated.Member
public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -361,7 +401,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
"extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
"hostDisplayId = " + mHostDisplayId + ", " +
- "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
+ "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
+ "serviceSupported = " + mServiceSupported + ", " +
+ "clientSupported = " + mClientSupported +
" }";
}
@@ -385,7 +427,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
&& extrasEquals(that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
&& mHostDisplayId == that.mHostDisplayId
- && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
+ && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
+ && mServiceSupported == that.mServiceSupported
+ && mClientSupported == that.mClientSupported;
}
@Override
@@ -403,6 +447,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + mHostDisplayId;
_hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
+ _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
+ _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
return _hash;
}
@@ -413,6 +459,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
int flg = 0;
+ if (mServiceSupported) flg |= 0x100;
+ if (mClientSupported) flg |= 0x200;
if (mHostInputToken != null) flg |= 0x20;
if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
dest.writeInt(flg);
@@ -438,6 +486,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
int flg = in.readInt();
+ boolean serviceSupported = (flg & 0x100) != 0;
+ boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
@@ -464,6 +514,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -497,6 +549,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @Nullable IBinder mHostInputToken;
private int mHostDisplayId;
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+ private boolean mServiceSupported;
+ private boolean mClientSupported;
private long mBuilderFieldsSet = 0L;
@@ -629,7 +683,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
/**
- * Specifies the UI specification for the inline suggestion tooltip in the response.
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
*/
@DataClass.Generated.Member
public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -639,10 +695,38 @@ public final class InlineSuggestionsRequest implements Parcelable {
return this;
}
+ /**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setServiceSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mServiceSupported = value;
+ return this;
+ }
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setClientSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mClientSupported = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x100; // Mark builder used
+ mBuilderFieldsSet |= 0x400; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -665,6 +749,12 @@ public final class InlineSuggestionsRequest implements Parcelable {
if ((mBuilderFieldsSet & 0x80) == 0) {
mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
}
+ if ((mBuilderFieldsSet & 0x100) == 0) {
+ mServiceSupported = defaultServiceSupported();
+ }
+ if ((mBuilderFieldsSet & 0x200) == 0) {
+ mClientSupported = defaultClientSupported();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mInlinePresentationSpecs,
@@ -673,12 +763,14 @@ public final class InlineSuggestionsRequest implements Parcelable {
mExtras,
mHostInputToken,
mHostDisplayId,
- mInlineTooltipPresentationSpec);
+ mInlineTooltipPresentationSpec,
+ mServiceSupported,
+ mClientSupported);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x100) != 0) {
+ if ((mBuilderFieldsSet & 0x400) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -686,10 +778,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1621415989607L,
- codegenVersion = "1.0.23",
+ time = 1615798784918L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index d2db0df6c597..5b2068ff16cd 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -96,8 +96,6 @@ public interface InputMethod {
*
* @param token special token for the system to identify
* {@link InputMethodService}
- * @param displayId The id of the display that current IME shown.
- * Used for {{@link #updateInputMethodDisplay(int)}}
* @param privilegedOperations IPC endpoint to do some privileged
* operations that are allowed only to the
* current IME.
@@ -105,9 +103,8 @@ public interface InputMethod {
* @hide
*/
@MainThread
- default void initializeInternal(IBinder token, int displayId,
+ default void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
- updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -143,16 +140,6 @@ public interface InputMethod {
public void attachToken(IBinder token);
/**
- * Update context display according to given displayId.
- *
- * @param displayId The id of the display that need to update for context.
- * @hide
- */
- @MainThread
- default void updateInputMethodDisplay(int displayId) {
- }
-
- /**
* Bind a new application environment in to the input method, so that it
* can later start and stop input processing.
* Typically this method is called when this input method is enabled in an
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 42d77cd09689..80a6c02f6389 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,7 +18,6 @@ package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.util.imetracing.ImeTracing.PROTO_ARG;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
@@ -73,7 +72,6 @@ import android.util.Pools.SimplePool;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.ImeFocusController;
@@ -88,6 +86,7 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -958,9 +957,10 @@ public final class InputMethodManager {
final boolean fullscreen = msg.arg1 != 0;
InputConnection ic = null;
synchronized (mH) {
- mFullscreenMode = fullscreen;
- if (mServedInputConnectionWrapper != null) {
+ if (mFullscreenMode != fullscreen
+ && mServedInputConnectionWrapper != null) {
ic = mServedInputConnectionWrapper.getInputConnection();
+ mFullscreenMode = fullscreen;
}
}
if (ic != null) {
@@ -3192,7 +3192,7 @@ public final class InputMethodManager {
}
for (String arg : args) {
- if (arg.equals(PROTO_ARG)) {
+ if (arg.equals(ImeTracing.PROTO_ARG)) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
dumpDebug(proto, null /* icProto */);
proto.flush();
@@ -3211,7 +3211,7 @@ public final class InputMethodManager {
* @hide
*/
@GuardedBy("mH")
- public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) {
+ public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) {
if (mCurrentInputMethodSession == null) {
return;
}
@@ -3237,7 +3237,7 @@ public final class InputMethodManager {
mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION);
}
if (icProto != null) {
- proto.write(INPUT_CONNECTION_CALL, icProto.getBytes());
+ proto.write(INPUT_CONNECTION_CALL, icProto);
}
}
}
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 1e8253db888a..604979b1ac78 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -27,10 +27,10 @@ import android.os.Parcelable;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
/**
* Represents the result of language detection of a piece of text.
@@ -168,7 +168,7 @@ public final class TextLanguage implements Parcelable {
public Builder putLocale(
@NonNull ULocale locale,
@FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
- Preconditions.checkNotNull(locale);
+ Objects.requireNonNull(locale);
mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
return this;
}
@@ -187,7 +187,7 @@ public final class TextLanguage implements Parcelable {
*/
@NonNull
public Builder setExtras(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
return this;
}
@@ -316,7 +316,7 @@ public final class TextLanguage implements Parcelable {
* @param text the text to process.
*/
public Builder(@NonNull CharSequence text) {
- mText = Preconditions.checkNotNull(text);
+ mText = Objects.requireNonNull(text);
}
/**
@@ -324,7 +324,7 @@ public final class TextLanguage implements Parcelable {
*/
@NonNull
public Builder setExtras(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
return this;
}
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index be49fc434c79..91412d7e8631 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -178,12 +178,23 @@ public class WebViewLibraryLoader {
*/
static void reserveAddressSpaceInZygote() {
System.loadLibrary("webviewchromium_loader");
- boolean is64Bit = VMRuntime.getRuntime().is64Bit();
- // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
- // On 32-bit it's fairly scarce and we should keep it to a realistic number that
- // permits some future growth but doesn't hog space: we use 130MB which is roughly
- // what was calculated on older OS versions in practice.
- long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 130 * 1024 * 1024;
+ long addressSpaceToReserve;
+ if (VMRuntime.getRuntime().is64Bit()) {
+ // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
+ addressSpaceToReserve = 1 * 1024 * 1024 * 1024;
+ } else if (VMRuntime.getRuntime().vmInstructionSet().equals("arm")) {
+ // On 32-bit the address space is fairly scarce, hence we should keep it to a realistic
+ // number that permits some future growth but doesn't hog space. For ARM we use 130MB
+ // which is roughly what was calculated on older OS versions. The size has been
+ // growing gradually, but a few efforts have offset it back to the size close to the
+ // original.
+ addressSpaceToReserve = 130 * 1024 * 1024;
+ } else {
+ // The number below was obtained for a binary used for x86 emulators, allowing some
+ // natural growth.
+ addressSpaceToReserve = 190 * 1024 * 1024;
+ }
+
sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
if (sAddressSpaceReserved) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index f9f823b70810..26579c5dec68 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -480,6 +480,17 @@ public interface WebViewProvider {
return false;
}
+ /**
+ * @see View#onApplyWindowInsets(WindowInsets).
+ *
+ * <p>This is the entry point for the WebView implementation to override. It returns
+ * {@code null} when the WebView implementation hasn't implemented the WindowInsets support
+ * on S yet. In this case, the {@link View#onApplyWindowInsets()} super method will be
+ * called instead.
+ *
+ * @param insets Insets to apply
+ * @return The supplied insets with any applied insets consumed.
+ */
@SuppressWarnings("unused")
@Nullable
default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index d8f7f4c5f326..f3dfda5bdf82 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -412,24 +412,29 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
void refreshChildren() {
- if (mAdapter == null) return;
+ final int adapterCount = mAdapter == null ? 0 : getCount();
for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
int index = modulo(i, getWindowSize());
- int adapterCount = getCount();
- // get the fresh child from the adapter
- final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
+ final View updatedChild;
+ if (i < adapterCount) {
+ // get the fresh child from the adapter
+ updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
- if (updatedChild.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ if (updatedChild.getImportantForAccessibility()
+ == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ } else {
+ updatedChild = null;
}
if (mViewsMap.containsKey(index)) {
final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
- // add the new child to the frame, if it exists
+ // flush out the old child
+ fl.removeAllViewsInLayout();
if (updatedChild != null) {
- // flush out the old child
- fl.removeAllViewsInLayout();
+ // add the new child to the frame, if it exists
fl.addView(updatedChild);
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0c4da88bb058..62585c1a40d1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -126,13 +126,13 @@ import android.widget.TextView.Drawables;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.EditableInputConnection;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.view.FloatingActionMode;
-import com.android.internal.widget.EditableInputConnection;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 8aa557bab4e3..f292c610f842 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -136,6 +136,7 @@ public class ImageView extends View {
private int[] mState = null;
private boolean mMergeState = false;
+ private boolean mHasLevelSet = false;
private int mLevel = 0;
@UnsupportedAppUsage
private int mDrawableWidth;
@@ -798,6 +799,7 @@ public class ImageView extends View {
@android.view.RemotableViewMethod
public void setImageLevel(int level) {
mLevel = level;
+ mHasLevelSet = true;
if (mDrawable != null) {
mDrawable.setLevel(level);
resizeFromDrawable();
@@ -1069,7 +1071,9 @@ public class ImageView extends View {
: isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
d.setVisible(visible, true);
}
- d.setLevel(mLevel);
+ if (mHasLevelSet) {
+ d.setLevel(mLevel);
+ }
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
applyImageTint();
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 1b76ebf7c8c6..356d059ba5dc 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -245,8 +245,6 @@ public class ProgressBar extends View {
private boolean mAggregatedIsVisible;
- private CharSequence mCustomStateDescription = null;
-
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
private ObjectAnimator mLastProgressAnimator;
@@ -685,6 +683,9 @@ public class ProgressBar extends View {
swapCurrentDrawable(mProgressDrawable);
stopAnimation();
}
+
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -1622,18 +1623,21 @@ public class ProgressBar extends View {
@Override
@RemotableViewMethod
public void setStateDescription(@Nullable CharSequence stateDescription) {
- mCustomStateDescription = stateDescription;
- if (stateDescription == null) {
- super.setStateDescription(formatStateDescription(mProgress));
- } else {
- super.setStateDescription(stateDescription);
- }
+ // Assume the previous custom state description is different from default state description.
+ // Otherwise when the argument is null to restore the default state description, we will
+ // send out a state description changed event even though the state description presented to
+ // the user doesn't change. Since mStateDescription in View is private, we can't prevent
+ // this event from sending out.
+ super.setStateDescription(stateDescription);
}
void onProgressRefresh(float scale, boolean fromUser, int progress) {
if (AccessibilityManager.getInstance(mContext).isEnabled()
- && mCustomStateDescription == null) {
- super.setStateDescription(formatStateDescription(mProgress));
+ && getStateDescription() == null && !isIndeterminate()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
+ sendAccessibilityEventUnchecked(event);
}
}
@@ -2356,7 +2360,15 @@ public class ProgressBar extends View {
AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(),
getProgress());
info.setRangeInfo(rangeInfo);
- info.setStateDescription(formatStateDescription(mProgress));
+ }
+
+ // Only set the default state description when custom state descripton is null.
+ if (getStateDescription() == null) {
+ if (isIndeterminate()) {
+ info.setStateDescription(getResources().getString(R.string.in_progress));
+ } else {
+ info.setStateDescription(formatStateDescription(mProgress));
+ }
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ca6e735f86b4..5500ff035ccb 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -200,13 +200,14 @@ import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.EditableInputConnection;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
-import com.android.internal.widget.EditableInputConnection;
import libcore.util.EmptyArray;
@@ -6318,6 +6319,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
text = TextUtils.stringOrSpannedString(text);
}
+ @AccessibilityUtils.A11yTextChangeType int a11yTextChangeType = AccessibilityUtils.NONE;
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ a11yTextChangeType = AccessibilityUtils.textOrSpanChanged(text, mText);
+ }
+
if (mAutoLinkMask != 0) {
Spannable s2;
@@ -6337,6 +6343,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* setText() again to try to upgrade the buffer type.
*/
setTextInternal(text);
+ if (a11yTextChangeType == AccessibilityUtils.NONE) {
+ a11yTextChangeType = AccessibilityUtils.PARCELABLE_SPAN;
+ }
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
@@ -6401,7 +6410,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
- notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ if (a11yTextChangeType == AccessibilityUtils.TEXT) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ } else if (a11yTextChangeType == AccessibilityUtils.PARCELABLE_SPAN) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java
new file mode 100644
index 000000000000..9a079751553f
--- /dev/null
+++ b/core/java/android/window/ConfigurationHelper.java
@@ -0,0 +1,132 @@
+/*
+ * 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.window;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ResourcesManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * A helper class to maintain {@link android.content.res.Configuration} related methods used both
+ * in {@link android.app.Activity} and {@link WindowContext}.
+ *
+ * @hide
+ */
+public class ConfigurationHelper {
+ private ConfigurationHelper() {}
+
+ /** Ask text layout engine to free its caches if there is a locale change. */
+ public static void freeTextLayoutCachesIfNeeded(int configDiff) {
+ if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ Canvas.freeTextLayoutCaches();
+ }
+ }
+
+ /**
+ * A helper method to filter out {@link ActivityInfo#CONFIG_SCREEN_SIZE} if the
+ * {@link Configuration#diffPublicOnly(Configuration) diff} of two {@link Configuration}
+ * doesn't cross the boundary.
+ *
+ * @see SizeConfigurationBuckets#filterDiff(int, Configuration, Configuration,
+ * SizeConfigurationBuckets)
+ */
+ public static int diffPublicWithSizeBuckets(@Nullable Configuration currentConfig,
+ @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
+ // If current configuration is null, it is definitely different from updated Configuration.
+ if (currentConfig == null) {
+ return 0xffffffff;
+ }
+ int publicDiff = currentConfig.diffPublicOnly(newConfig);
+ return SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig, newConfig, buckets);
+ }
+
+ /**
+ * Returns {@code true} if the {@link android.content.res.Resources} associated with
+ * a {@code token} needs to be updated.
+ *
+ * @param token A {@link Context#getActivityToken() activity token} or
+ * {@link Context#getWindowContextToken() window context token}
+ * @param config The original {@link Configuration}
+ * @param newConfig The updated Configuration
+ * @param displayChanged a flag to indicate there's a display change
+ * @param configChanged a flag to indicate there's a Configuration change.
+ *
+ * @see ResourcesManager#updateResourcesForActivity(IBinder, Configuration, int)
+ */
+ public static boolean shouldUpdateResources(IBinder token, @Nullable Configuration config,
+ @NonNull Configuration newConfig, @NonNull Configuration overrideConfig,
+ boolean displayChanged, @Nullable Boolean configChanged) {
+ // The configuration has not yet been initialized. We should update it.
+ if (config == null) {
+ return true;
+ }
+ // If the token associated context is moved to another display, we should update the
+ // ResourcesKey.
+ if (displayChanged) {
+ return true;
+ }
+ // If the new config is the same as the config this Activity is already running with and
+ // the override config also didn't change, then don't update the Resources
+ if (!ResourcesManager.getInstance().isSameResourcesOverrideConfig(token, overrideConfig)) {
+ return true;
+ }
+ // If there's a update on WindowConfiguration#mBounds or maxBounds, we should update the
+ // Resources to make WindowMetrics API report the updated result.
+ if (shouldUpdateWindowMetricsBounds(config, newConfig)) {
+ return true;
+ }
+ return configChanged == null ? config.diff(newConfig) != 0 : configChanged;
+ }
+
+ /**
+ * Returns {@code true} if {@code displayId} is different from {@code newDisplayId}.
+ * Note that {@link Display#INVALID_DISPLAY} means no difference.
+ */
+ public static boolean isDifferentDisplay(int displayId, int newDisplayId) {
+ return newDisplayId != INVALID_DISPLAY && displayId != newDisplayId;
+ }
+
+ // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
+ // constructions.
+ /**
+ * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
+ * should be updated.
+ *
+ * @see WindowManager#getCurrentWindowMetrics()
+ * @see WindowManager#getMaximumWindowMetrics()
+ */
+ private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
+ @NonNull Configuration newConfig) {
+ final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
+ final Rect newBounds = newConfig.windowConfiguration.getBounds();
+
+ final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
+ final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
+
+ return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
+ }
+}
diff --git a/core/java/android/window/DisplayAreaInfo.java b/core/java/android/window/DisplayAreaInfo.java
index 358467ff599f..1a7aab6852b6 100644
--- a/core/java/android/window/DisplayAreaInfo.java
+++ b/core/java/android/window/DisplayAreaInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.content.res.Configuration;
@@ -43,8 +45,17 @@ public final class DisplayAreaInfo implements Parcelable {
*/
public final int displayId;
+ /**
+ * The feature id of this display area.
+ */
public final int featureId;
+ /**
+ * The feature id of the root display area this display area is associated with.
+ * @hide
+ */
+ public int rootDisplayAreaId = FEATURE_UNDEFINED;
+
public DisplayAreaInfo(@NonNull WindowContainerToken token, int displayId, int featureId) {
this.token = token;
this.displayId = displayId;
@@ -56,6 +67,7 @@ public final class DisplayAreaInfo implements Parcelable {
configuration.readFromParcel(in);
displayId = in.readInt();
featureId = in.readInt();
+ rootDisplayAreaId = in.readInt();
}
@Override
@@ -64,6 +76,7 @@ public final class DisplayAreaInfo implements Parcelable {
configuration.writeToParcel(dest, flags);
dest.writeInt(displayId);
dest.writeInt(featureId);
+ dest.writeInt(rootDisplayAreaId);
}
@NonNull
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 878439906de2..e6746556fb67 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -34,6 +34,15 @@ import java.util.concurrent.Executor;
public class DisplayAreaOrganizer extends WindowOrganizer {
/**
+ * Key to specify the {@link com.android.server.wm.RootDisplayArea} to attach a window to.
+ * It will be used by the function passed in from
+ * {@link com.android.server.wm.DisplayAreaPolicyBuilder#setSelectRootForWindowFunc(BiFunction)}
+ * to find the Root DA to attach the window.
+ * @hide
+ */
+ public static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
+
+ /**
* The value in display area indicating that no value has been set.
*/
public static final int FEATURE_UNDEFINED = -1;
diff --git a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
index 02aa1a93a35f..7864c245310e 100644
--- a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
+++ b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
@@ -16,14 +16,18 @@
package android.window;
+import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
/**
* Interface to be invoked by the controlling process when a remote transition has finished.
*
* @see IRemoteTransition
+ * @param wct An optional WindowContainerTransaction to apply before the transition finished.
+ * @param sct An optional Surface Transaction that is added to the end of the finish/cleanup
+ * transaction. This is applied by shell.Transitions (before submitting the wct).
* {@hide}
*/
interface IRemoteTransitionFinishedCallback {
- void onTransitionFinished(in WindowContainerTransaction wct);
+ void onTransitionFinished(in WindowContainerTransaction wct, in SurfaceControl.Transaction sct);
}
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
new file mode 100644
index 000000000000..5eb432e785ee
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -0,0 +1,52 @@
+/**
+ * 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.window;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+
+/** @hide */
+oneway interface ITaskFragmentOrganizer {
+ void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
+ void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
+
+ /**
+ * Called when the parent leaf Task of organized TaskFragments is changed.
+ * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+ * transaction.
+ *
+ * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+ * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+ * bounds.
+ */
+ void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
+
+ /**
+ * Called when the {@link WindowContainerTransaction} created with
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+ *
+ * @param errorCallbackToken Token set through {@link
+ * WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param exceptionBundle Bundle containing the exception. Should be created with
+ * {@link TaskFragmentOrganizer#putExceptionInBundle}.
+ */
+ void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
+}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
new file mode 100644
index 000000000000..0ca8a864dba5
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizerController.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.window;
+
+import android.window.ITaskFragmentOrganizer;
+
+/** @hide */
+interface ITaskFragmentOrganizerController {
+
+ /**
+ * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ */
+ void registerOrganizer(in ITaskFragmentOrganizer organizer);
+
+ /**
+ * Unregisters a previously registered TaskFragmentOrganizer.
+ */
+ void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
+}
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 1223d72f643e..39cdf5af50ce 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -20,6 +20,7 @@ import android.view.SurfaceControl;
import android.os.IBinder;
import android.window.IDisplayAreaOrganizerController;
+import android.window.ITaskFragmentOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
@@ -77,6 +78,9 @@ interface IWindowOrganizerController {
/** @return An interface enabling the management of display area organizers. */
IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
+ /** @return An interface enabling the management of task fragment organizers. */
+ ITaskFragmentOrganizerController getTaskFragmentOrganizerController();
+
/**
* Registers a transition player with Core. There is only one of these at a time and calling
* this will replace the existing one if set.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/core/java/android/window/TaskFragmentAppearedInfo.aidl
index 0037059e2c51..3729c09168a6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
+++ b/core/java/android/window/TaskFragmentAppearedInfo.aidl
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
-
-import android.content.ComponentName
-import com.android.server.wm.traces.common.windowmanager.WindowManagerState
-import com.android.server.wm.traces.parser.toWindowName
+package android.window;
/**
- * Checks that an activity [activity] is in PIP mode
+ * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * @hide
*/
-fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
- val windowName = activity.toWindowName()
- return isInPipMode(windowName)
-}
+parcelable TaskFragmentAppearedInfo;
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
new file mode 100644
index 000000000000..234b30c0662c
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAppearedInfo.java
@@ -0,0 +1,86 @@
+/*
+ * 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.window;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * @hide
+ */
+public final class TaskFragmentAppearedInfo implements Parcelable {
+
+ @NonNull
+ private final TaskFragmentInfo mTaskFragmentInfo;
+
+ @NonNull
+ private final SurfaceControl mLeash;
+
+ public TaskFragmentAppearedInfo(
+ @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
+ mTaskFragmentInfo = taskFragmentInfo;
+ mLeash = leash;
+ }
+
+ public TaskFragmentInfo getTaskFragmentInfo() {
+ return mTaskFragmentInfo;
+ }
+
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+
+ private TaskFragmentAppearedInfo(Parcel in) {
+ mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+ mLeash = in.readTypedObject(SurfaceControl.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mTaskFragmentInfo, flags);
+ dest.writeTypedObject(mLeash, flags);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentAppearedInfo> CREATOR =
+ new Creator<TaskFragmentAppearedInfo>() {
+ @Override
+ public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
+ return new TaskFragmentAppearedInfo(in);
+ }
+
+ @Override
+ public TaskFragmentAppearedInfo[] newArray(int size) {
+ return new TaskFragmentAppearedInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentAppearedInfo{"
+ + " taskFragmentInfo=" + mTaskFragmentInfo
+ + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/window/TaskFragmentCreationParams.aidl b/core/java/android/window/TaskFragmentCreationParams.aidl
new file mode 100644
index 000000000000..fde50892640b
--- /dev/null
+++ b/core/java/android/window/TaskFragmentCreationParams.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.window;
+
+/**
+ * Data object for options to create TaskFragment with.
+ * @hide
+ */
+parcelable TaskFragmentCreationParams;
diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java
new file mode 100644
index 000000000000..e4d6a6c07835
--- /dev/null
+++ b/core/java/android/window/TaskFragmentCreationParams.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.window;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.WindowingMode;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data object for options to create TaskFragment with.
+ * @hide
+ */
+public final class TaskFragmentCreationParams implements Parcelable {
+
+ /** The organizer that will organize this TaskFragment. */
+ @NonNull
+ private final ITaskFragmentOrganizer mOrganizer;
+
+ /**
+ * Unique token assigned from the client organizer to identify the {@link TaskFragmentInfo} when
+ * a new TaskFragment is created with this option.
+ */
+ @NonNull
+ private final IBinder mFragmentToken;
+
+ /**
+ * Activity token used to identify the leaf Task to create the TaskFragment in. It has to belong
+ * to the same app as the root Activity of the target Task.
+ */
+ @NonNull
+ private final IBinder mOwnerToken;
+
+ /** The initial bounds of the TaskFragment. Fills parent if empty. */
+ @NonNull
+ private final Rect mInitialBounds = new Rect();
+
+ /** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */
+ @WindowingMode
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+ private TaskFragmentCreationParams(
+ @NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken,
+ @NonNull IBinder ownerToken) {
+ mOrganizer = organizer;
+ mFragmentToken = fragmentToken;
+ mOwnerToken = ownerToken;
+ }
+
+ public ITaskFragmentOrganizer getOrganizer() {
+ return mOrganizer;
+ }
+
+ public IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ public IBinder getOwnerToken() {
+ return mOwnerToken;
+ }
+
+ public Rect getInitialBounds() {
+ return mInitialBounds;
+ }
+
+ @WindowingMode
+ public int getWindowingMode() {
+ return mWindowingMode;
+ }
+
+ private TaskFragmentCreationParams(Parcel in) {
+ mOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
+ mFragmentToken = in.readStrongBinder();
+ mOwnerToken = in.readStrongBinder();
+ mInitialBounds.readFromParcel(in);
+ mWindowingMode = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongInterface(mOrganizer);
+ dest.writeStrongBinder(mFragmentToken);
+ dest.writeStrongBinder(mOwnerToken);
+ mInitialBounds.writeToParcel(dest, flags);
+ dest.writeInt(mWindowingMode);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentCreationParams> CREATOR =
+ new Creator<TaskFragmentCreationParams>() {
+ @Override
+ public TaskFragmentCreationParams createFromParcel(Parcel in) {
+ return new TaskFragmentCreationParams(in);
+ }
+
+ @Override
+ public TaskFragmentCreationParams[] newArray(int size) {
+ return new TaskFragmentCreationParams[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentCreationParams{"
+ + " organizer=" + mOrganizer
+ + " fragmentToken=" + mFragmentToken
+ + " ownerToken=" + mOwnerToken
+ + " initialBounds=" + mInitialBounds
+ + " windowingMode=" + mWindowingMode
+ + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Builder to construct the options to create TaskFragment with. */
+ public static class Builder {
+
+ @NonNull
+ private final ITaskFragmentOrganizer mOrganizer;
+
+ @NonNull
+ private final IBinder mFragmentToken;
+
+ @NonNull
+ private final IBinder mOwnerToken;
+
+ @NonNull
+ private final Rect mInitialBounds = new Rect();
+
+ @WindowingMode
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+ public Builder(@NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken,
+ @NonNull IBinder ownerToken) {
+ mOrganizer = organizer;
+ mFragmentToken = fragmentToken;
+ mOwnerToken = ownerToken;
+ }
+
+ /** Sets the initial bounds for the TaskFragment. */
+ public Builder setInitialBounds(@NonNull Rect bounds) {
+ mInitialBounds.set(bounds);
+ return this;
+ }
+
+ /** Sets the initial windowing mode for the TaskFragment. */
+ public Builder setWindowingMode(@WindowingMode int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
+ /** Constructs the options to create TaskFragment with. */
+ public TaskFragmentCreationParams build() {
+ final TaskFragmentCreationParams result = new TaskFragmentCreationParams(
+ mOrganizer, mFragmentToken, mOwnerToken);
+ result.mInitialBounds.set(mInitialBounds);
+ result.mWindowingMode = mWindowingMode;
+ return result;
+ }
+ }
+}
diff --git a/core/java/android/window/TaskFragmentInfo.aidl b/core/java/android/window/TaskFragmentInfo.aidl
new file mode 100644
index 000000000000..461a7364803f
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.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.window;
+
+/**
+ * Stores information about a particular TaskFragment.
+ * @hide
+ */
+parcelable TaskFragmentInfo;
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
new file mode 100644
index 000000000000..6d4a2f130129
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -0,0 +1,170 @@
+/*
+ * 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.window;
+
+import static android.app.WindowConfiguration.WindowingMode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Stores information about a particular TaskFragment.
+ * @hide
+ */
+public final class TaskFragmentInfo implements Parcelable {
+
+ /**
+ * Client assigned unique token in {@link TaskFragmentCreationParams#fragmentToken} to create
+ * this TaskFragment with.
+ */
+ @NonNull
+ private final IBinder mFragmentToken;
+
+ @NonNull
+ private final WindowContainerToken mToken;
+
+ @NonNull
+ private final Configuration mConfiguration = new Configuration();
+
+ /** Whether the TaskFragment contains any child Activity. */
+ private final boolean mIsEmpty;
+
+ /** Whether this TaskFragment is visible on the window hierarchy. */
+ private final boolean mIsVisible;
+
+ /**
+ * List of Activity tokens that are children of this TaskFragment. It only contains Activities
+ * that belong to the organizer process for security.
+ */
+ private final List<IBinder> mActivities = new ArrayList<>();
+
+ public TaskFragmentInfo(
+ @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
+ @NonNull Configuration configuration, boolean isEmpty, boolean isVisible,
+ List<IBinder> activities) {
+ if (fragmentToken == null) {
+ throw new IllegalArgumentException("Invalid TaskFragmentInfo.");
+ }
+ mFragmentToken = fragmentToken;
+ mToken = token;
+ mConfiguration.setTo(configuration);
+ mIsEmpty = isEmpty;
+ mIsVisible = isVisible;
+ mActivities.addAll(activities);
+ }
+
+ public IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ public WindowContainerToken getToken() {
+ return mToken;
+ }
+
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ public boolean isEmpty() {
+ return mIsEmpty;
+ }
+
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+
+ public List<IBinder> getActivities() {
+ return mActivities;
+ }
+
+ @WindowingMode
+ public int getWindowingMode() {
+ return mConfiguration.windowConfiguration.getWindowingMode();
+ }
+
+ /**
+ * Returns {@code true} if the parameters that are important for task fragment organizers are
+ * equal between this {@link TaskFragmentInfo} and {@param that}.
+ */
+ public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentInfo that) {
+ if (that == null) {
+ return false;
+ }
+
+ return mFragmentToken.equals(that.mFragmentToken)
+ && mToken.equals(that.mToken)
+ && mIsEmpty == that.mIsEmpty
+ && mIsVisible == that.mIsVisible
+ && getWindowingMode() == that.getWindowingMode()
+ && mActivities.equals(that.mActivities);
+ }
+
+ private TaskFragmentInfo(Parcel in) {
+ mFragmentToken = in.readStrongBinder();
+ mToken = in.readTypedObject(WindowContainerToken.CREATOR);
+ mConfiguration.readFromParcel(in);
+ mIsEmpty = in.readBoolean();
+ mIsVisible = in.readBoolean();
+ in.readBinderList(mActivities);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mFragmentToken);
+ dest.writeTypedObject(mToken, flags);
+ mConfiguration.writeToParcel(dest, flags);
+ dest.writeBoolean(mIsEmpty);
+ dest.writeBoolean(mIsVisible);
+ dest.writeBinderList(mActivities);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentInfo> CREATOR =
+ new Creator<TaskFragmentInfo>() {
+ @Override
+ public TaskFragmentInfo createFromParcel(Parcel in) {
+ return new TaskFragmentInfo(in);
+ }
+
+ @Override
+ public TaskFragmentInfo[] newArray(int size) {
+ return new TaskFragmentInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentInfo{"
+ + " fragmentToken=" + mFragmentToken
+ + " token=" + mToken
+ + " isEmpty=" + mIsEmpty
+ + " isVisible=" + mIsVisible
+ + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
new file mode 100644
index 000000000000..3b4d4e52c908
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -0,0 +1,170 @@
+/*
+ * 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.window;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Interface for WindowManager to delegate control of {@link com.android.server.wm.TaskFragment}.
+ * @hide
+ */
+public class TaskFragmentOrganizer extends WindowOrganizer {
+
+ /**
+ * Key to the exception in {@link Bundle} in {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+ */
+ private static final String KEY_ERROR_CALLBACK_EXCEPTION = "fragment_exception";
+
+ /**
+ * Creates a {@link Bundle} with an exception that can be passed to
+ * {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+ */
+ public static Bundle putExceptionInBundle(@NonNull Throwable exception) {
+ final Bundle exceptionBundle = new Bundle();
+ exceptionBundle.putSerializable(KEY_ERROR_CALLBACK_EXCEPTION, exception);
+ return exceptionBundle;
+ }
+
+ /**
+ * Callbacks from WM Core are posted on this executor.
+ */
+ private final Executor mExecutor;
+
+ public TaskFragmentOrganizer(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
+
+ /**
+ * Gets the executor to run callbacks on.
+ */
+ @NonNull
+ public Executor getExecutor() {
+ return mExecutor;
+ }
+
+ /**
+ * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ */
+ @CallSuper
+ public void registerOrganizer() {
+ try {
+ getController().registerOrganizer(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a previously registered TaskFragmentOrganizer.
+ */
+ @CallSuper
+ public void unregisterOrganizer() {
+ try {
+ getController().unregisterOrganizer(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Called when a TaskFragment is created and organized by this organizer. */
+ public void onTaskFragmentAppeared(
+ @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+
+ /** Called when the status of an organized TaskFragment is changed. */
+ public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
+
+ /** Called when an organized TaskFragment is removed. */
+ public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {}
+
+ /**
+ * Called when the parent leaf Task of organized TaskFragments is changed.
+ * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+ * transaction.
+ *
+ * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+ * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+ * bounds.
+ */
+ public void onTaskFragmentParentInfoChanged(
+ @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
+
+ /**
+ * Called when the {@link WindowContainerTransaction} created with
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+ *
+ * @param errorCallbackToken token set in
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param exception exception from the server side.
+ */
+ public void onTaskFragmentError(
+ @NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
+
+ private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
+ @Override
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
+ }
+
+ @Override
+ public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
+ }
+
+ @Override
+ public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
+ }
+
+ @Override
+ public void onTaskFragmentParentInfoChanged(
+ @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
+ fragmentToken, parentConfig));
+ }
+
+ @Override
+ public void onTaskFragmentError(
+ @NonNull IBinder errorCallbackToken, @NonNull Bundle exceptionBundle) {
+ mExecutor.execute(() -> TaskFragmentOrganizer.this.onTaskFragmentError(
+ errorCallbackToken,
+ (Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
+ }
+ };
+
+ public ITaskFragmentOrganizer getIOrganizer() {
+ return mInterface;
+ }
+
+ private ITaskFragmentOrganizerController getController() {
+ try {
+ return getWindowOrganizerController().getTaskFragmentOrganizerController();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index c7c91cdd0941..8fa011028f44 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -222,6 +222,7 @@ public class TaskOrganizer extends WindowOrganizer {
}
}
+
/**
* Restarts the top activity in the given task by killing its process if it is visible.
* @hide
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 141f47b130d1..6351b0395020 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -18,6 +18,7 @@ package android.window;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
@@ -33,6 +34,18 @@ import android.os.Parcelable;
*/
public final class TransitionFilter implements Parcelable {
+ /** The associated requirement doesn't care about the z-order. */
+ public static final int CONTAINER_ORDER_ANY = 0;
+ /** The associated requirement only matches the top-most (z-order) container. */
+ public static final int CONTAINER_ORDER_TOP = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "CONTAINER_ORDER_" }, value = {
+ CONTAINER_ORDER_ANY,
+ CONTAINER_ORDER_TOP,
+ })
+ public @interface ContainerOrder {}
+
/**
* When non-null: this is a list of transition types that this filter applies to. This filter
* will fail for transitions that aren't one of these types.
@@ -126,6 +139,7 @@ public final class TransitionFilter implements Parcelable {
public static final class Requirement implements Parcelable {
public int mActivityType = ACTIVITY_TYPE_UNDEFINED;
public int[] mModes = null;
+ public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY;
public Requirement() {
}
@@ -133,6 +147,7 @@ public final class TransitionFilter implements Parcelable {
private Requirement(Parcel in) {
mActivityType = in.readInt();
mModes = in.createIntArray();
+ mOrder = in.readInt();
}
/** Go through changes and find if at-least one change matches this filter */
@@ -143,6 +158,9 @@ public final class TransitionFilter implements Parcelable {
// Only look at independent animating windows.
continue;
}
+ if (mOrder == CONTAINER_ORDER_TOP && i > 0) {
+ continue;
+ }
if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
if (change.getTaskInfo() == null
|| change.getTaskInfo().getActivityType() != mActivityType) {
@@ -166,7 +184,7 @@ public final class TransitionFilter implements Parcelable {
/** Check if the request matches this filter. It may generate false positives */
boolean matches(@NonNull TransitionRequestInfo request) {
- // Can't check modes since the transition hasn't been built at this point.
+ // Can't check modes/order since the transition hasn't been built at this point.
if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true;
return request.getTriggerTask() != null
&& request.getTriggerTask().getActivityType() == mActivityType;
@@ -177,6 +195,7 @@ public final class TransitionFilter implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mActivityType);
dest.writeIntArray(mModes);
+ dest.writeInt(mOrder);
}
@NonNull
@@ -209,7 +228,17 @@ public final class TransitionFilter implements Parcelable {
out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i]));
}
}
- return out.append("]}").toString();
+ out.append("]").toString();
+ out.append(" order=" + containerOrderToString(mOrder));
+ return out.toString();
+ }
+ }
+
+ private static String containerOrderToString(int order) {
+ switch (order) {
+ case CONTAINER_ORDER_ANY: return "ANY";
+ case CONTAINER_ORDER_TOP: return "TOP";
}
+ return "UNKNOWN(" + order + ")";
}
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 23b8ee4a019f..ebc66efad0f7 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -16,6 +16,12 @@
package android.window;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -31,6 +37,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
@@ -80,8 +87,11 @@ public final class TransitionInfo implements Parcelable {
/** The container has voice session. */
public static final int FLAG_IS_VOICE_INTERACTION = 1 << 4;
+ /** The container is the display. */
+ public static final int FLAG_IS_DISPLAY = 1 << 5;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 5;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 6;
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
@@ -91,6 +101,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_TRANSLUCENT,
FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT,
FLAG_IS_VOICE_INTERACTION,
+ FLAG_IS_DISPLAY,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -102,6 +113,8 @@ public final class TransitionInfo implements Parcelable {
private SurfaceControl mRootLeash;
private final Point mRootOffset = new Point();
+ private AnimationOptions mOptions;
+
/** @hide */
public TransitionInfo(@WindowManager.TransitionOldType int type,
@WindowManager.TransitionFlags int flags) {
@@ -116,6 +129,7 @@ public final class TransitionInfo implements Parcelable {
mRootLeash = new SurfaceControl();
mRootLeash.readFromParcel(in);
mRootOffset.readFromParcel(in);
+ mOptions = in.readTypedObject(AnimationOptions.CREATOR);
}
@Override
@@ -126,6 +140,7 @@ public final class TransitionInfo implements Parcelable {
dest.writeList(mChanges);
mRootLeash.writeToParcel(dest, flags);
mRootOffset.writeToParcel(dest, flags);
+ dest.writeTypedObject(mOptions, flags);
}
@NonNull
@@ -154,6 +169,10 @@ public final class TransitionInfo implements Parcelable {
mRootOffset.set(offsetLeft, offsetTop);
}
+ public void setAnimationOptions(AnimationOptions options) {
+ mOptions = options;
+ }
+
public int getType() {
return mType;
}
@@ -182,6 +201,10 @@ public final class TransitionInfo implements Parcelable {
return mRootOffset;
}
+ public AnimationOptions getAnimationOptions() {
+ return mOptions;
+ }
+
@NonNull
public List<Change> getChanges() {
return mChanges;
@@ -257,6 +280,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "IS_VOICE_INTERACTION");
}
+ if ((flags & FLAG_IS_DISPLAY) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "IS_DISPLAY");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
}
@@ -484,4 +510,146 @@ public final class TransitionInfo implements Parcelable {
+ mStartRotation + "->" + mEndRotation + "}";
}
}
+
+ /** Represents animation options during a transition */
+ public static final class AnimationOptions implements Parcelable {
+
+ private int mType;
+ private int mEnterResId;
+ private int mExitResId;
+ private boolean mOverrideTaskTransition;
+ private String mPackageName;
+ private final Rect mTransitionBounds = new Rect();
+ private HardwareBuffer mThumbnail;
+
+ private AnimationOptions(int type) {
+ mType = type;
+ }
+
+ public AnimationOptions(Parcel in) {
+ mType = in.readInt();
+ mEnterResId = in.readInt();
+ mExitResId = in.readInt();
+ mOverrideTaskTransition = in.readBoolean();
+ mPackageName = in.readString();
+ mTransitionBounds.readFromParcel(in);
+ mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
+ }
+
+ public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
+ int exitResId, boolean overrideTaskTransition) {
+ AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
+ options.mPackageName = packageName;
+ options.mEnterResId = enterResId;
+ options.mExitResId = exitResId;
+ options.mOverrideTaskTransition = overrideTaskTransition;
+ return options;
+ }
+
+ public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width,
+ int height) {
+ AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL);
+ options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+ return options;
+ }
+
+ public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width,
+ int height) {
+ AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP);
+ options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+ return options;
+ }
+
+ public static AnimationOptions makeThumnbnailAnimOptions(HardwareBuffer srcThumb,
+ int startX, int startY, boolean scaleUp) {
+ AnimationOptions options = new AnimationOptions(
+ scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN);
+ options.mTransitionBounds.set(startX, startY, startX, startY);
+ options.mThumbnail = srcThumb;
+ return options;
+ }
+
+ public static AnimationOptions makeCrossProfileAnimOptions() {
+ AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS);
+ return options;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getEnterResId() {
+ return mEnterResId;
+ }
+
+ public int getExitResId() {
+ return mExitResId;
+ }
+
+ public boolean getOverrideTaskTransition() {
+ return mOverrideTaskTransition;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public Rect getTransitionBounds() {
+ return mTransitionBounds;
+ }
+
+ public HardwareBuffer getThumbnail() {
+ return mThumbnail;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mEnterResId);
+ dest.writeInt(mExitResId);
+ dest.writeBoolean(mOverrideTaskTransition);
+ dest.writeString(mPackageName);
+ mTransitionBounds.writeToParcel(dest, flags);
+ dest.writeTypedObject(mThumbnail, flags);
+ }
+
+ @NonNull
+ public static final Creator<AnimationOptions> CREATOR =
+ new Creator<AnimationOptions>() {
+ @Override
+ public AnimationOptions createFromParcel(Parcel in) {
+ return new AnimationOptions(in);
+ }
+
+ @Override
+ public AnimationOptions[] newArray(int size) {
+ return new AnimationOptions[size];
+ }
+ };
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ private static String typeToString(int mode) {
+ switch(mode) {
+ case ANIM_CUSTOM: return "ANIM_CUSTOM";
+ case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL";
+ case ANIM_SCALE_UP: return "ANIM_SCALE_UP";
+ case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP";
+ case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN";
+ case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS";
+ default: return "<unknown:" + mode + ">";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
+ + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
+ }
+ }
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index c0af57214e5e..9c512add3345 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.WindowConfiguration;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -48,11 +49,15 @@ public final class WindowContainerTransaction implements Parcelable {
// Flat list because re-order operations are order-dependent
private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
+ @Nullable
+ private IBinder mErrorCallbackToken;
+
public WindowContainerTransaction() {}
private WindowContainerTransaction(Parcel in) {
in.readMap(mChanges, null /* loader */);
in.readList(mHierarchyOps, null /* loader */);
+ mErrorCallbackToken = in.readStrongBinder();
}
private Change getOrCreateChange(IBinder token) {
@@ -325,7 +330,8 @@ public final class WindowContainerTransaction implements Parcelable {
/**
* Sets to containers adjacent to each other. Containers below two visible adjacent roots will
- * be made invisible. This currently only applies to Task containers created by organizer.
+ * be made invisible. This currently only applies to TaskFragment containers created by
+ * organizer.
* @param root1 the first root.
* @param root2 the second root.
*/
@@ -378,6 +384,119 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Creates a new TaskFragment with the given options.
+ * @param taskFragmentOptions the options used to create the TaskFragment.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction createTaskFragment(
+ @NonNull TaskFragmentCreationParams taskFragmentOptions) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT)
+ .setTaskFragmentCreationOptions(taskFragmentOptions)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed.
+ * @param taskFragment the TaskFragment to be removed.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction deleteTaskFragment(
+ @NonNull WindowContainerToken taskFragment) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT)
+ .setContainer(taskFragment.asBinder())
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Starts an activity in the TaskFragment.
+ * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+ * {@link TaskFragmentCreationParams#fragmentToken}.
+ * @param activityIntent intent to start the activity.
+ * @param activityOptions ActivityOptions to start the activity with.
+ * @see android.content.Context#startActivity(Intent, Bundle).
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction startActivityInTaskFragment(
+ @NonNull IBinder fragmentToken, @NonNull Intent activityIntent,
+ @Nullable Bundle activityOptions) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT)
+ .setContainer(fragmentToken)
+ .setActivityIntent(activityIntent)
+ .setLaunchOptions(activityOptions)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Moves an activity into the TaskFragment.
+ * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+ * {@link TaskFragmentCreationParams#fragmentToken}.
+ * @param activityToken activity to be reparented.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction reparentActivityToTaskFragment(
+ @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT)
+ .setReparentContainer(fragmentToken)
+ .setContainer(activityToken)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Reparents all children of one TaskFragment to another.
+ * @param oldParent children of this TaskFragment will be reparented.
+ * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the
+ * children will be moved to the leaf Task.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction reparentChildren(
+ @NonNull WindowContainerToken oldParent,
+ @Nullable WindowContainerToken newParent) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
+ .setContainer(oldParent.asBinder())
+ .setReparentContainer(newParent.asBinder())
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
+ * trigger callback with this {@param errorCallbackToken}.
+ * @param errorCallbackToken client provided token that will be passed back as parameter in
+ * the callback if there is an error on the server side.
+ * @see ITaskFragmentOrganizer#onTaskFragmentError
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
+ if (mErrorCallbackToken != null) {
+ throw new IllegalStateException("Can't set multiple error token for one transaction.");
+ }
+ mErrorCallbackToken = errorCallbackToken;
+ return this;
+ }
+
+ /**
* Merges another WCT into this one.
* @param transfer When true, this will transfer everything from other potentially leaving
* other in an unusable state. When false, other is left alone, but
@@ -398,6 +517,13 @@ public final class WindowContainerTransaction implements Parcelable {
mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
: new HierarchyOp(other.mHierarchyOps.get(i)));
}
+ if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
+ != other.mErrorCallbackToken) {
+ throw new IllegalArgumentException("Can't merge two WCT with different error token");
+ }
+ mErrorCallbackToken = mErrorCallbackToken != null
+ ? mErrorCallbackToken
+ : other.mErrorCallbackToken;
}
/** @hide */
@@ -415,11 +541,17 @@ public final class WindowContainerTransaction implements Parcelable {
return mHierarchyOps;
}
+ /** @hide */
+ @Nullable
+ public IBinder getErrorCallbackToken() {
+ return mErrorCallbackToken;
+ }
+
@Override
@NonNull
public String toString() {
return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
- + " }";
+ + " errorCallbackToken=" + mErrorCallbackToken + " }";
}
@Override
@@ -427,6 +559,7 @@ public final class WindowContainerTransaction implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mChanges);
dest.writeList(mHierarchyOps);
+ dest.writeStrongBinder(mErrorCallbackToken);
}
@Override
@@ -705,6 +838,11 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5;
public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6;
+ public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7;
+ public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8;
+ public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9;
+ public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10;
+ public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -713,75 +851,98 @@ public final class WindowContainerTransaction implements Parcelable {
private final int mType;
// Container we are performing the operation on.
- private final IBinder mContainer;
+ @Nullable
+ private IBinder mContainer;
// If this is same as mContainer, then only change position, don't reparent.
- private final IBinder mReparent;
+ @Nullable
+ private IBinder mReparent;
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
- private final boolean mToTop;
+ private boolean mToTop;
+
+ @Nullable
+ private int[] mWindowingModes;
+
+ @Nullable
+ private int[] mActivityTypes;
+
+ @Nullable
+ private Bundle mLaunchOptions;
- final private int[] mWindowingModes;
- final private int[] mActivityTypes;
+ @Nullable
+ private Intent mActivityIntent;
- private final Bundle mLaunchOptions;
+ // Used as options for WindowContainerTransaction#createTaskFragment().
+ @Nullable
+ private TaskFragmentCreationParams mTaskFragmentCreationOptions;
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,
- container, reparent, null, null, toTop, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
+ .setContainer(container)
+ .setReparentContainer(reparent)
+ .setToTop(toTop)
+ .build();
}
public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER,
- container, container, null, null, toTop, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
+ .setContainer(container)
+ .setReparentContainer(container)
+ .setToTop(toTop)
+ .build();
}
public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,
- currentParent, newParent, windowingModes, activityTypes, onTop, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
+ .setContainer(currentParent)
+ .setReparentContainer(newParent)
+ .setWindowingModes(windowingModes)
+ .setActivityTypes(activityTypes)
+ .setToTop(onTop)
+ .build();
}
public static HierarchyOp createForSetLaunchRoot(IBinder container,
int[] windowingModes, int[] activityTypes) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT,
- container, null, windowingModes, activityTypes, false, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT)
+ .setContainer(container)
+ .setWindowingModes(windowingModes)
+ .setActivityTypes(activityTypes)
+ .build();
}
public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
- root1, root2, null, null, false, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
+ .setContainer(root1)
+ .setReparentContainer(root2)
+ .build();
}
/** Create a hierarchy op for launching a task. */
public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
final Bundle fullOptions = options == null ? new Bundle() : options;
fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
- return new HierarchyOp(HIERARCHY_OP_TYPE_LAUNCH_TASK, null, null, null, null, true,
- fullOptions);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+ .setToTop(true)
+ .setLaunchOptions(fullOptions)
+ .build();
}
/** Create a hierarchy op for setting launch adjacent flag root. */
public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
boolean clearRoot) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null,
- null, null, clearRoot, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT)
+ .setContainer(container)
+ .setToTop(clearRoot)
+ .build();
}
-
- private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent,
- int[] windowingModes, int[] activityTypes, boolean toTop,
- @Nullable Bundle launchOptions) {
+ /** Only creates through {@link Builder}. */
+ private HierarchyOp(int type) {
mType = type;
- mContainer = container;
- mReparent = reparent;
- mWindowingModes = windowingModes != null ?
- Arrays.copyOf(windowingModes, windowingModes.length) : null;
- mActivityTypes = activityTypes != null ?
- Arrays.copyOf(activityTypes, activityTypes.length) : null;
- mToTop = toTop;
- mLaunchOptions = launchOptions;
}
public HierarchyOp(@NonNull HierarchyOp copy) {
@@ -792,6 +953,8 @@ public final class WindowContainerTransaction implements Parcelable {
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
+ mActivityIntent = copy.mActivityIntent;
+ mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
}
protected HierarchyOp(Parcel in) {
@@ -802,6 +965,8 @@ public final class WindowContainerTransaction implements Parcelable {
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
+ mActivityIntent = in.readTypedObject(Intent.CREATOR);
+ mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
}
public int getType() {
@@ -844,6 +1009,16 @@ public final class WindowContainerTransaction implements Parcelable {
return mLaunchOptions;
}
+ @Nullable
+ public Intent getActivityIntent() {
+ return mActivityIntent;
+ }
+
+ @Nullable
+ public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
+ return mTaskFragmentCreationOptions;
+ }
+
@Override
public String toString() {
switch (mType) {
@@ -868,6 +1043,19 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
+ "}";
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}";
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+ return "{DeleteTaskFragment: taskFragment=" + mContainer + "}";
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+ return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent="
+ + mActivityIntent + " options=" + mLaunchOptions + "}";
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+ return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent
+ + " activity=" + mContainer + "}";
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent
+ + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
@@ -884,6 +1072,8 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
+ dest.writeTypedObject(mActivityIntent, flags);
+ dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
}
@Override
@@ -902,5 +1092,96 @@ public final class WindowContainerTransaction implements Parcelable {
return new HierarchyOp[size];
}
};
+
+ private static class Builder {
+
+ private final int mType;
+
+ @Nullable
+ private IBinder mContainer;
+
+ @Nullable
+ private IBinder mReparent;
+
+ private boolean mToTop;
+
+ @Nullable
+ private int[] mWindowingModes;
+
+ @Nullable
+ private int[] mActivityTypes;
+
+ @Nullable
+ private Bundle mLaunchOptions;
+
+ @Nullable
+ private Intent mActivityIntent;
+
+ @Nullable
+ private TaskFragmentCreationParams mTaskFragmentCreationOptions;
+
+ Builder(int type) {
+ mType = type;
+ }
+
+ Builder setContainer(@Nullable IBinder container) {
+ mContainer = container;
+ return this;
+ }
+
+ Builder setReparentContainer(@Nullable IBinder reparentContainer) {
+ mReparent = reparentContainer;
+ return this;
+ }
+
+ Builder setToTop(boolean toTop) {
+ mToTop = toTop;
+ return this;
+ }
+
+ Builder setWindowingModes(@Nullable int[] windowingModes) {
+ mWindowingModes = windowingModes;
+ return this;
+ }
+
+ Builder setActivityTypes(@Nullable int[] activityTypes) {
+ mActivityTypes = activityTypes;
+ return this;
+ }
+
+ Builder setLaunchOptions(@Nullable Bundle launchOptions) {
+ mLaunchOptions = launchOptions;
+ return this;
+ }
+
+ Builder setActivityIntent(@Nullable Intent activityIntent) {
+ mActivityIntent = activityIntent;
+ return this;
+ }
+
+ Builder setTaskFragmentCreationOptions(
+ @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
+ mTaskFragmentCreationOptions = taskFragmentCreationOptions;
+ return this;
+ }
+
+ HierarchyOp build() {
+ final HierarchyOp hierarchyOp = new HierarchyOp(mType);
+ hierarchyOp.mContainer = mContainer;
+ hierarchyOp.mReparent = mReparent;
+ hierarchyOp.mWindowingModes = mWindowingModes != null
+ ? Arrays.copyOf(mWindowingModes, mWindowingModes.length)
+ : null;
+ hierarchyOp.mActivityTypes = mActivityTypes != null
+ ? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
+ : null;
+ hierarchyOp.mToTop = mToTop;
+ hierarchyOp.mLaunchOptions = mLaunchOptions;
+ hierarchyOp.mActivityIntent = mActivityIntent;
+ hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
+
+ return hierarchyOp;
+ }
+ }
}
}
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 901625b0732c..5d400853540f 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.os.IBinder;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -57,8 +56,14 @@ public class WindowContext extends ContextWrapper {
*
* @param base Base {@link Context} for this new instance.
* @param type Window type to be used with this context.
- * @param options A bundle used to pass window-related options.
- *
+ * @param options A bundle used to pass window-related options. For example, on device with
+ * multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window
+ * using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options.
+ * Example usage:
+ * Bundle options = new Bundle();
+ * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+ * Context windowContext = context.createWindowContext(display, type, options);
+ * @see DisplayAreaInfo#rootDisplayAreaId
* @hide
*/
public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
@@ -67,7 +72,7 @@ public class WindowContext extends ContextWrapper {
mType = type;
mOptions = options;
mWindowManager = createWindowContextWindowManager(this);
- IBinder token = getWindowContextToken();
+ WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
mController = new WindowContextController(token);
Reference.reachabilityFence(this);
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index d84f571931fd..5aa623388574 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,6 +19,7 @@ package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -46,7 +47,7 @@ public class WindowContextController {
@VisibleForTesting
public boolean mAttachedToDisplayArea;
@NonNull
- private final IBinder mToken;
+ private final WindowTokenClient mToken;
/**
* Window Context Controller constructor
@@ -54,14 +55,13 @@ public class WindowContextController {
* @param token The token used to attach to a window manager node. It is usually from
* {@link Context#getWindowContextToken()}.
*/
- public WindowContextController(@NonNull IBinder token) {
- mToken = token;
- mWms = WindowManagerGlobal.getWindowManagerService();
+ public WindowContextController(@NonNull WindowTokenClient token) {
+ this(token, WindowManagerGlobal.getWindowManagerService());
}
/** Used for test only. DO NOT USE it in production code. */
@VisibleForTesting
- public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) {
+ public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
mWms = mockWms;
}
@@ -81,8 +81,15 @@ public class WindowContextController {
+ "a DisplayArea once.");
}
try {
- mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId,
- options);
+ final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
+ displayId, options);
+ if (configuration != null) {
+ mAttachedToDisplayArea = true;
+ // Send the DisplayArea's configuration to WindowContext directly instead of
+ // waiting for dispatching from WMS.
+ mToken.onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index b8619fbcf334..5171adf168ce 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -36,7 +36,6 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.view.WindowManagerImpl;
-// TODO(b/159767464): handle #onConfigurationChanged(Configuration)
/**
* A {@link Service} responsible for showing a non-activity window, such as software keyboards or
* accessibility overlay windows. This {@link Service} has similar behavior to
@@ -54,6 +53,7 @@ public abstract class WindowProviderService extends Service {
private final WindowTokenClient mWindowToken = new WindowTokenClient();
private final WindowContextController mController = new WindowContextController(mWindowToken);
private WindowManager mWindowManager;
+ private boolean mInitialized;
/**
* Returns the type of this {@link WindowProviderService}.
@@ -90,6 +90,20 @@ public abstract class WindowProviderService extends Service {
}
/**
+ * Returns the display ID to launch this {@link WindowProviderService}.
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint({"OnNameExpected"})
+ // Suppress the lint because it is not a callback and users may override this API to provide
+ // display.
+ @NonNull
+ public int getInitialDisplayId() {
+ return DEFAULT_DISPLAY;
+ }
+
+ /**
* Attaches this WindowProviderService to the {@code windowToken}.
*
* @hide
@@ -104,19 +118,22 @@ public abstract class WindowProviderService extends Service {
public final Context createServiceBaseContext(ActivityThread mainThread,
LoadedApk packageInfo) {
final Context context = super.createServiceBaseContext(mainThread, packageInfo);
- // Always associate with the default display at initialization.
- final Display defaultDisplay = context.getSystemService(DisplayManager.class)
- .getDisplay(DEFAULT_DISPLAY);
- return context.createTokenContext(mWindowToken, defaultDisplay);
+ final Display display = context.getSystemService(DisplayManager.class)
+ .getDisplay(getInitialDisplayId());
+ return context.createTokenContext(mWindowToken, display);
}
- @CallSuper
+ /** @hide */
@Override
- public void onCreate() {
- super.onCreate();
- mWindowToken.attachContext(this);
- mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions());
- mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+ if (!mInitialized) {
+ mWindowToken.attachContext(this);
+ mController.attachToDisplayArea(getWindowType(), getDisplayId(),
+ getWindowContextOptions());
+ mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+ mInitialized = true;
+ }
}
@SuppressLint("OnNameExpected")
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 6abf5575d353..284b4b98b4c9 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,14 +15,25 @@
*/
package android.window;
+import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
+import static android.window.ConfigurationHelper.isDifferentDisplay;
+import static android.window.ConfigurationHelper.shouldUpdateResources;
+
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.inputmethodservice.AbstractInputMethodService;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Debug;
import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;
@@ -33,11 +44,13 @@ import java.lang.ref.WeakReference;
* {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}.
*
* @see WindowContext
- * @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle)
+ * @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
*
* @hide
*/
public class WindowTokenClient extends IWindowToken.Stub {
+ private static final String TAG = WindowTokenClient.class.getSimpleName();
+
/**
* Attached {@link Context} for this window token to update configuration and resources.
* Initialized by {@link #attachContext(Context)}.
@@ -46,12 +59,16 @@ public class WindowTokenClient extends IWindowToken.Stub {
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+ private final Configuration mConfiguration = new Configuration();
+
+ private boolean mShouldDumpConfigForIme;
+
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
* <p>This method must be called before invoking
- * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
- * Bundle, boolean)}.<p/>
+ * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
+ * Bundle)}.<p/>
*
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
@@ -61,25 +78,85 @@ public class WindowTokenClient extends IWindowToken.Stub {
throw new IllegalStateException("Context is already attached.");
}
mContextRef = new WeakReference<>(context);
+ mConfiguration.setTo(context.getResources().getConfiguration());
+ mShouldDumpConfigForIme = Build.IS_DEBUGGABLE
+ && context instanceof AbstractInputMethodService;
}
+ /**
+ * Called when {@link Configuration} updates from the server side receive.
+ *
+ * @param newConfig the updated {@link Configuration}
+ * @param newDisplayId the updated {@link android.view.Display} ID
+ */
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+ }
+
+ /**
+ * Called when {@link Configuration} updates from the server side receive.
+ *
+ * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
+ * whether to dispatch configuration update or not.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
+ boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
if (context == null) {
return;
}
- final int currentDisplayId = context.getDisplayId();
- final boolean displayChanged = newDisplayId != currentDisplayId;
- final Configuration config = context.getResources().getConfiguration();
- final boolean configChanged = config.diff(newConfig) != 0;
- if (displayChanged || configChanged) {
+ final boolean displayChanged = isDifferentDisplay(context.getDisplayId(), newDisplayId);
+ final boolean shouldUpdateResources = shouldUpdateResources(this, mConfiguration,
+ newConfig, newConfig /* overrideConfig */, displayChanged,
+ null /* configChanged */);
+
+ if (!shouldUpdateResources && mShouldDumpConfigForIme) {
+ Log.d(TAG, "Configuration not dispatch to IME because configuration is up"
+ + " to date. Current config=" + context.getResources().getConfiguration()
+ + ", reported config=" + mConfiguration
+ + ", updated config=" + newConfig);
+ }
+
+ if (shouldUpdateResources) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
- if (context instanceof WindowContext) {
+
+ if (shouldReportConfigChange && context instanceof WindowContext) {
+ final WindowContext windowContext = (WindowContext) context;
ActivityThread.currentActivityThread().getHandler().post(
- () -> ((WindowContext) context).dispatchConfigurationChanged(newConfig));
+ () -> windowContext.dispatchConfigurationChanged(newConfig));
+ }
+
+ // Dispatch onConfigurationChanged only if there's a significant public change to
+ // make it compatible with the original behavior.
+ final Configuration[] sizeConfigurations = context.getResources()
+ .getSizeConfigurations();
+ final SizeConfigurationBuckets buckets = sizeConfigurations != null
+ ? new SizeConfigurationBuckets(sizeConfigurations) : null;
+ final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
+
+ if (shouldReportConfigChange && diff != 0
+ && context instanceof WindowProviderService) {
+ final WindowProviderService windowProviderService = (WindowProviderService) context;
+ ActivityThread.currentActivityThread().getHandler().post(
+ () -> windowProviderService.onConfigurationChanged(newConfig));
+ }
+ freeTextLayoutCachesIfNeeded(diff);
+ if (mShouldDumpConfigForIme) {
+ if (!shouldReportConfigChange) {
+ Log.d(TAG, "Only apply configuration update to Resources because "
+ + "shouldReportConfigChange is false.\n" + Debug.getCallers(5));
+ } else if (diff == 0) {
+ Log.d(TAG, "Configuration not dispatch to IME because configuration has no "
+ + " public difference with updated config. "
+ + " Current config=" + context.getResources().getConfiguration()
+ + ", reported config=" + mConfiguration
+ + ", updated config=" + newConfig);
+ }
}
+ mConfiguration.setTo(newConfig);
}
if (displayChanged) {
context.updateDisplay(newDisplayId);
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 4b4e20f9181b..6a976efa5ef9 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -20,16 +20,23 @@ import static com.android.internal.accessibility.common.ShortcutConstants.Access
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.ParcelableSpan;
+import android.text.Spanned;
import android.text.TextUtils;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityManager;
+import libcore.util.EmptyArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -39,7 +46,25 @@ import java.util.Set;
* Collection of utilities for accessibility service.
*/
public final class AccessibilityUtils {
- private AccessibilityUtils() {}
+ private AccessibilityUtils() {
+ }
+
+ /** @hide */
+ @IntDef(value = {
+ NONE,
+ TEXT,
+ PARCELABLE_SPAN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface A11yTextChangeType {
+ }
+
+ /** Specifies no content has been changed for accessibility. */
+ public static final int NONE = 0;
+ /** Specifies some readable sequence has been changed. */
+ public static final int TEXT = 1;
+ /** Specifies some parcelable spans has been changed. */
+ public static final int PARCELABLE_SPAN = 2;
/**
* Returns the set of enabled accessibility services for userId. If there are no
@@ -168,4 +193,55 @@ public final class AccessibilityUtils {
Settings.Secure.USER_SETUP_COMPLETE, /* def= */ 0, UserHandle.USER_CURRENT)
!= /* false */ 0;
}
+
+ /**
+ * Returns the text change type for accessibility. It only cares about readable sequence changes
+ * or {@link ParcelableSpan} changes which are able to pass via IPC.
+ *
+ * @param before The CharSequence before changing
+ * @param after The CharSequence after changing
+ * @return Returns {@code TEXT} for readable sequence changes or {@code PARCELABLE_SPAN} for
+ * ParcelableSpan changes. Otherwise, returns {@code NONE}.
+ */
+ @A11yTextChangeType
+ public static int textOrSpanChanged(CharSequence before, CharSequence after) {
+ if (!TextUtils.equals(before, after)) {
+ return TEXT;
+ }
+ if (before instanceof Spanned || after instanceof Spanned) {
+ if (!parcelableSpansEquals(before, after)) {
+ return PARCELABLE_SPAN;
+ }
+ }
+ return NONE;
+ }
+
+ private static boolean parcelableSpansEquals(CharSequence before, CharSequence after) {
+ Object[] spansA = EmptyArray.OBJECT;
+ Object[] spansB = EmptyArray.OBJECT;
+ Spanned a = null;
+ Spanned b = null;
+ if (before instanceof Spanned) {
+ a = (Spanned) before;
+ spansA = a.getSpans(0, a.length(), ParcelableSpan.class);
+ }
+ if (after instanceof Spanned) {
+ b = (Spanned) after;
+ spansB = b.getSpans(0, b.length(), ParcelableSpan.class);
+ }
+ if (spansA.length != spansB.length) {
+ return false;
+ }
+ for (int i = 0; i < spansA.length; ++i) {
+ final Object thisSpan = spansA[i];
+ final Object otherSpan = spansB[i];
+ if ((thisSpan.getClass() != otherSpan.getClass())
+ || (a.getSpanStart(thisSpan) != b.getSpanStart(otherSpan))
+ || (a.getSpanEnd(thisSpan) != b.getSpanEnd(otherSpan))
+ || (a.getSpanFlags(thisSpan) != b.getSpanFlags(otherSpan))) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 6bf46457a1de..1d13b73fc186 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -145,7 +145,7 @@ interface IBatteryStats {
void noteBleScanStarted(in WorkSource ws, boolean isUnoptimized);
void noteBleScanStopped(in WorkSource ws, boolean isUnoptimized);
- void noteResetBleScan();
+ void noteBleScanReset();
void noteBleScanResults(in WorkSource ws, int numNewResults);
/** {@hide} */
diff --git a/core/java/com/android/internal/content/om/OWNERS b/core/java/com/android/internal/content/om/OWNERS
new file mode 100644
index 000000000000..44fd9fd2a3c2
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 568631
+include /core/java/android/content/om/OWNERS
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 02ffe8c5268e..410e68bf3581 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2007-2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.widget;
+package com.android.internal.inputmethod;
import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE;
import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT;
@@ -22,13 +22,11 @@ import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT;
import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END;
import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.text.Editable;
import android.text.Selection;
import android.text.method.KeyListener;
import android.util.Log;
-import android.util.imetracing.InputConnectionHelper;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
@@ -41,9 +39,9 @@ import android.widget.TextView;
/**
* Base class for an editable InputConnection instance. This is created by {@link TextView} or
- * {@link EditText}.
+ * {@link android.widget.EditText}.
*/
-public class EditableInputConnection extends BaseInputConnection
+public final class EditableInputConnection extends BaseInputConnection
implements DumpableInputConnection {
private static final boolean DEBUG = false;
private static final String TAG = "EditableInputConnection";
@@ -55,7 +53,6 @@ public class EditableInputConnection extends BaseInputConnection
// A negative value means that this connection has been finished by the InputMethodManager.
private int mBatchEditNesting;
- @UnsupportedAppUsage
public EditableInputConnection(TextView textview) {
super(textview, true);
mTextView = textview;
@@ -72,7 +69,7 @@ public class EditableInputConnection extends BaseInputConnection
@Override
public boolean beginBatchEdit() {
- synchronized(this) {
+ synchronized (this) {
if (mBatchEditNesting >= 0) {
mTextView.beginBatchEdit();
mBatchEditNesting++;
@@ -84,7 +81,7 @@ public class EditableInputConnection extends BaseInputConnection
@Override
public boolean endBatchEdit() {
- synchronized(this) {
+ synchronized (this) {
if (mBatchEditNesting > 0) {
// When the connection is reset by the InputMethodManager and reportFinish
// is called, some endBatchEdit calls may still be asynchronously received from the
@@ -107,7 +104,7 @@ public class EditableInputConnection extends BaseInputConnection
@Override
public void closeConnection() {
super.closeConnection();
- synchronized(this) {
+ synchronized (this) {
while (mBatchEditNesting > 0) {
endBatchEdit();
}
@@ -159,7 +156,7 @@ public class EditableInputConnection extends BaseInputConnection
mTextView.onEditorAction(actionCode);
return true;
}
-
+
@Override
public boolean performContextMenuAction(int id) {
if (DEBUG) Log.v(TAG, "performContextMenuAction " + id);
@@ -168,13 +165,13 @@ public class EditableInputConnection extends BaseInputConnection
mTextView.endBatchEdit();
return true;
}
-
+
@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
if (mTextView != null) {
ExtractedText et = new ExtractedText();
if (mTextView.extractText(request, et)) {
- if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) {
+ if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) {
mTextView.setExtracting(request);
}
return et;
@@ -213,14 +210,13 @@ public class EditableInputConnection extends BaseInputConnection
// It is possible that any other bit is used as a valid flag in a future release.
// We should reject the entire request in such a case.
- final int KNOWN_FLAGS_MASK = InputConnection.CURSOR_UPDATE_IMMEDIATE |
- InputConnection.CURSOR_UPDATE_MONITOR;
- final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK;
+ final int knownFlagMask = InputConnection.CURSOR_UPDATE_IMMEDIATE
+ | InputConnection.CURSOR_UPDATE_MONITOR;
+ final int unknownFlags = cursorUpdateMode & ~knownFlagMask;
if (unknownFlags != 0) {
if (DEBUG) {
- Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." +
- " cursorUpdateMode=" + cursorUpdateMode +
- " unknownFlags=" + unknownFlags);
+ Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags. "
+ + "cursorUpdateMode=" + cursorUpdateMode + " unknownFlags=" + unknownFlags);
}
return false;
}
@@ -264,7 +260,7 @@ public class EditableInputConnection extends BaseInputConnection
final long token = proto.start(fieldId);
CharSequence editableText = mTextView.getText();
CharSequence selectedText = getSelectedText(0 /* flags */);
- if (InputConnectionHelper.DUMP_TEXT) {
+ if (InputConnectionProtoDumper.DUMP_TEXT) {
if (editableText != null) {
proto.write(EDITABLE_TEXT, editableText.toString());
}
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
new file mode 100644
index 000000000000..2ad1373b07b0
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -0,0 +1,532 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputContentInfo;
+
+import com.android.internal.view.IInputContext;
+
+import java.util.Objects;
+
+/**
+ * A stateless wrapper of {@link com.android.internal.view.IInputContext} to encapsulate boilerplate
+ * code around {@link Completable} and {@link RemoteException}.
+ */
+public final class IInputContextInvoker {
+
+ @NonNull
+ private final IInputContext mIInputContext;
+
+ private IInputContextInvoker(@NonNull IInputContext inputContext) {
+ mIInputContext = inputContext;
+ }
+
+ /**
+ * Creates a new instance of {@link IInputContextInvoker} for the given {@link IInputContext}.
+ *
+ * @param inputContext {@link IInputContext} to be wrapped.
+ * @return A new instance of {@link IInputContextInvoker}.
+ */
+ public static IInputContextInvoker create(@NonNull IInputContext inputContext) {
+ Objects.requireNonNull(inputContext);
+ return new IInputContextInvoker(inputContext);
+ }
+
+ /**
+ * Invokes {@link IInputContext#getTextAfterCursor(int, int,
+ * com.android.internal.inputmethod.ICharSequenceResultCallback)}.
+ *
+ * @param length {@code length} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.CharSequence getTextAfterCursor(int length, int flags) {
+ final Completable.CharSequence value = Completable.createCharSequence();
+ try {
+ mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getTextBeforeCursor(int, int, ICharSequenceResultCallback)}.
+ *
+ * @param length {@code length} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.CharSequence getTextBeforeCursor(int length, int flags) {
+ final Completable.CharSequence value = Completable.createCharSequence();
+ try {
+ mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getSelectedText(int, ICharSequenceResultCallback)}.
+ *
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.CharSequence getSelectedText(int flags) {
+ final Completable.CharSequence value = Completable.createCharSequence();
+ try {
+ mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes
+ * {@link IInputContext#getSurroundingText(int, int, int, ISurroundingTextResultCallback)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.SurroundingText} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.SurroundingText getSurroundingText(int beforeLength, int afterLength,
+ int flags) {
+ final Completable.SurroundingText value = Completable.createSurroundingText();
+ try {
+ mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
+ ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getCursorCapsMode(int, IIntResultCallback)}.
+ *
+ * @param reqModes {@code reqModes} parameter to be passed.
+ * @return {@link Completable.Int} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.Int getCursorCapsMode(int reqModes) {
+ final Completable.Int value = Completable.createInt();
+ try {
+ mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getExtractedText(ExtractedTextRequest, int,
+ * IExtractedTextResultCallback)}.
+ *
+ * @param request {@code request} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.ExtractedText} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ final Completable.ExtractedText value = Completable.createExtractedText();
+ try {
+ mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#commitText(CharSequence, int)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ try {
+ mIInputContext.commitText(text, newCursorPosition);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#commitCompletion(CompletionInfo)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitCompletion(CompletionInfo text) {
+ try {
+ mIInputContext.commitCompletion(text);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#commitCorrection(CorrectionInfo)}.
+ *
+ * @param correctionInfo {@code correctionInfo} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ try {
+ mIInputContext.commitCorrection(correctionInfo);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#setSelection(int, int)}.
+ *
+ * @param start {@code start} parameter to be passed.
+ * @param end {@code start} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setSelection(int start, int end) {
+ try {
+ mIInputContext.setSelection(start, end);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performEditorAction(int)}.
+ *
+ * @param actionCode {@code start} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performEditorAction(int actionCode) {
+ try {
+ mIInputContext.performEditorAction(actionCode);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performContextMenuAction(id)}.
+ *
+ * @param id {@code id} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performContextMenuAction(int id) {
+ try {
+ mIInputContext.performContextMenuAction(id);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#setComposingRegion(int, int)}.
+ *
+ * @param start {@code id} parameter to be passed.
+ * @param end {@code id} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingRegion(int start, int end) {
+ try {
+ mIInputContext.setComposingRegion(start, end);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#setComposingText(CharSequence, int)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ try {
+ mIInputContext.setComposingText(text, newCursorPosition);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#finishComposingText()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean finishComposingText() {
+ try {
+ mIInputContext.finishComposingText();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#beginBatchEdit()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean beginBatchEdit() {
+ try {
+ mIInputContext.beginBatchEdit();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#endBatchEdit()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean endBatchEdit() {
+ try {
+ mIInputContext.endBatchEdit();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#sendKeyEvent(KeyEvent)}.
+ *
+ * @param event {@code event} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean sendKeyEvent(KeyEvent event) {
+ try {
+ mIInputContext.sendKeyEvent(event);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#clearMetaKeyStates(int)}.
+ *
+ * @param states {@code states} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean clearMetaKeyStates(int states) {
+ try {
+ mIInputContext.clearMetaKeyStates(states);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#deleteSurroundingText(int, int)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ try {
+ mIInputContext.deleteSurroundingText(beforeLength, afterLength);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#deleteSurroundingTextInCodePoints(int, int)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ try {
+ mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performSpellCheck()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performSpellCheck() {
+ try {
+ mIInputContext.performSpellCheck();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performPrivateCommand(String, Bundle)}.
+ *
+ * @param action {@code action} parameter to be passed.
+ * @param data {@code data} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performPrivateCommand(String action, Bundle data) {
+ try {
+ mIInputContext.performPrivateCommand(action, data);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#requestUpdateCursorAnchorInfo(int, IIntResultCallback)}.
+ *
+ * @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
+ * @return {@link Completable.Int} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.Int requestUpdateCursorAnchorInfo(int cursorUpdateMode) {
+ final Completable.Int value = Completable.createInt();
+ try {
+ mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode,
+ ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes
+ * {@link IInputContext#commitContent(InputContentInfo, int, Bundle, IIntResultCallback)}.
+ *
+ * @param inputContentInfo {@code inputContentInfo} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @param opts {@code opts} parameter to be passed.
+ * @return {@link Completable.Int} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.Int commitContent(InputContentInfo inputContentInfo, int flags,
+ Bundle opts) {
+ final Completable.Int value = Completable.createInt();
+ try {
+ mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#setImeConsumesInput(boolean)}.
+ *
+ * @param imeConsumesInput {@code imeConsumesInput} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
+ try {
+ mIInputContext.setImeConsumesInput(imeConsumesInput);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
deleted file mode 100644
index 2f782baeb1d9..000000000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.os.IBinder;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-
-oneway interface IMultiClientInputMethod {
- void initialize(in IMultiClientInputMethodPrivilegedOperations privOps);
- void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
- void removeClient(int clientId);
-}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
deleted file mode 100644
index b5f2147784c0..000000000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.view.InputChannel;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-
-/**
- * Defines priviledged operations that only the current MSIMS is allowed to call.
- * Actual operations are implemented and handled by MultiClientInputMethodManagerService.
- */
-interface IMultiClientInputMethodPrivilegedOperations {
- IBinder createInputMethodWindowToken(int displayId);
- void deleteInputMethodWindowToken(IBinder token);
- void acceptClient(int clientId, in IInputMethodSession session,
- in IMultiClientInputMethodSession multiClientSession, in InputChannel writeChannel);
- void reportImeWindowTarget(int clientId, int targetWindowHandle, in IBinder imeWindowToken);
- boolean isUidAllowedOnDisplay(int displayId, int uid);
- void setActive(int clientId, boolean active);
-}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
deleted file mode 100644
index b40e82ce7a9f..000000000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.os.ResultReceiver;
-import android.view.inputmethod.EditorInfo;
-import com.android.internal.view.IInputContext;
-
-oneway interface IMultiClientInputMethodSession {
- void startInputOrWindowGainedFocus(
- in IInputContext inputContext, int missingMethods, in EditorInfo attribute,
- int controlFlags, int softInputMode, int targetWindowHandle);
- void showSoftInput(int flags, in ResultReceiver resultReceiver);
- void hideSoftInput(int flags, in ResultReceiver resultReceiver);
-}
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/com/android/internal/inputmethod/ImeTracing.java
index 4696ae325e7b..8b21b7ef10ea 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracing.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
-import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -36,8 +35,6 @@ import java.io.PrintWriter;
* An abstract class that declares the methods for ime trace related operations - enable trace,
* schedule trace and add new trace to buffer. Both the client and server side classes can use
* it by getting an implementation through {@link ImeTracing#getInstance()}.
- *
- * @hide
*/
public abstract class ImeTracing {
@@ -94,6 +91,28 @@ public abstract class ImeTracing {
}
/**
+ * Calling {@link IInputMethodManager#startImeTrace()}} to capture IME trace.
+ */
+ public final void startImeTrace() {
+ try {
+ mService.startImeTrace();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not start ime trace." + e);
+ }
+ }
+
+ /**
+ * Calling {@link IInputMethodManager#stopImeTrace()} to stop IME trace.
+ */
+ public final void stopImeTrace() {
+ try {
+ mService.stopImeTrace();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not stop ime trace." + e);
+ }
+ }
+
+ /**
* @param proto dump to be added to the buffer
*/
public abstract void addToBuffer(ProtoOutputStream proto, int source);
@@ -106,17 +125,33 @@ public abstract class ImeTracing {
* @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
*/
public abstract void triggerClientDump(String where, InputMethodManager immInstance,
- ProtoOutputStream icProto);
+ @Nullable byte[] icProto);
+
+ /**
+ * A delegate for {@link #triggerServiceDump(String, ServiceDumper, byte[])}.
+ */
+ @FunctionalInterface
+ public interface ServiceDumper {
+ /**
+ * Dumps internal data into {@link ProtoOutputStream}.
+ *
+ * @param proto {@link ProtoOutputStream} to be dumped into.
+ * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto
+ * format.
+ */
+ void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto);
+ }
/**
* Starts a proto dump of the currently connected InputMethodService information.
*
* @param where Place where the trace was triggered.
- * @param service The {@link android.inputmethodservice.InputMethodService} to be dumped.
+ * @param dumper {@link ServiceDumper} to be used to dump
+ * {@link android.inputmethodservice.InputMethodService}.
* @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
*/
- public abstract void triggerServiceDump(String where, AbstractInputMethodService service,
- ProtoOutputStream icProto);
+ public abstract void triggerServiceDump(String where, ServiceDumper dumper,
+ @Nullable byte[] icProto);
/**
* Starts a proto dump of the InputMethodManagerService information.
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
index 5a57a6ade98b..31d278b5ca54 100644
--- a/core/java/android/util/imetracing/ImeTracingClientImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import android.annotation.NonNull;
-import android.inputmethodservice.AbstractInputMethodService;
+import android.annotation.Nullable;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
@@ -27,7 +27,7 @@ import android.view.inputmethod.InputMethodManager;
import java.io.PrintWriter;
/**
- * @hide
+ * An implementation of {@link ImeTracing} for non system_server processes.
*/
class ImeTracingClientImpl extends ImeTracing {
ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
@@ -40,7 +40,7 @@ class ImeTracingClientImpl extends ImeTracing {
@Override
public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
- ProtoOutputStream icProto) {
+ @Nullable byte[] icProto) {
if (!isEnabled() || !isAvailable()) {
return;
}
@@ -64,8 +64,8 @@ class ImeTracingClientImpl extends ImeTracing {
}
@Override
- public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service,
- ProtoOutputStream icProto) {
+ public void triggerServiceDump(String where, @NonNull ServiceDumper dumper,
+ @Nullable byte[] icProto) {
if (!isEnabled() || !isAvailable()) {
return;
}
@@ -79,7 +79,7 @@ class ImeTracingClientImpl extends ImeTracing {
try {
ProtoOutputStream proto = new ProtoOutputStream();
- service.dumpProtoInternal(proto, icProto);
+ dumper.dumpToProto(proto, icProto);
sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where);
} catch (RemoteException e) {
Log.e(TAG, "Exception while sending ime-related service dump to server", e);
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
index 06e4c5002776..d452e60eb1dd 100644
--- a/core/java/android/util/imetracing/ImeTracingServerImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import static android.os.Build.IS_USER;
import android.annotation.Nullable;
-import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
@@ -37,7 +36,7 @@ import java.io.IOException;
import java.io.PrintWriter;
/**
- * @hide
+ * An implementation of {@link ImeTracing} for the system_server process.
*/
class ImeTracingServerImpl extends ImeTracing {
private static final String TRACE_DIRNAME = "/data/misc/wmtrace/";
@@ -107,13 +106,12 @@ class ImeTracingServerImpl extends ImeTracing {
@Override
public void triggerClientDump(String where, InputMethodManager immInstance,
- ProtoOutputStream icProto) {
+ @Nullable byte[] icProto) {
// Intentionally left empty, this is implemented in ImeTracingClientImpl
}
@Override
- public void triggerServiceDump(String where, AbstractInputMethodService service,
- ProtoOutputStream icProto) {
+ public void triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto) {
// Intentionally left empty, this is implemented in ImeTracingClientImpl
}
diff --git a/core/java/android/util/imetracing/InputConnectionHelper.java b/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java
index 39f1e01eb4a9..7172d0a41909 100644
--- a/core/java/android/util/imetracing/InputConnectionHelper.java
+++ b/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE;
import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT;
@@ -41,13 +41,12 @@ import android.view.inputmethod.SurroundingText;
/**
* Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are
* integrated into {@link ImeTracing}.
- * @hide
*/
-public class InputConnectionHelper {
- static final String TAG = "InputConnectionHelper";
+public final class InputConnectionProtoDumper {
+ static final String TAG = "InputConnectionProtoDumper";
public static final boolean DUMP_TEXT = false;
- private InputConnectionHelper() {}
+ private InputConnectionProtoDumper() {}
/**
* Builder for InputConnectionCallProto to hold
@@ -59,10 +58,11 @@ public class InputConnectionHelper {
* {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
* @param result The text after the cursor position; the length of the
* returned text might be less than <var>length</var>.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length,
- int flags, @Nullable CharSequence result) {
+ @NonNull
+ public static byte[] buildGetTextAfterCursorProto(@IntRange(from = 0) int length, int flags,
+ @Nullable CharSequence result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_TEXT_AFTER_CURSOR);
proto.write(GetTextAfterCursor.LENGTH, length);
@@ -73,7 +73,7 @@ public class InputConnectionHelper {
proto.write(GetTextAfterCursor.RESULT, result.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -86,9 +86,10 @@ public class InputConnectionHelper {
* {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
* @param result The text before the cursor position; the length of the
* returned text might be less than <var>length</var>.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
+ @NonNull
+ public static byte[] buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
int flags, @Nullable CharSequence result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_TEXT_BEFORE_CURSOR);
@@ -100,7 +101,7 @@ public class InputConnectionHelper {
proto.write(GetTextBeforeCursor.RESULT, result.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -114,10 +115,10 @@ public class InputConnectionHelper {
* no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
* later, returns false when the target application does not implement
* this method.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetSelectedTextProto(int flags,
- @Nullable CharSequence result) {
+ @NonNull
+ public static byte[] buildGetSelectedTextProto(int flags, @Nullable CharSequence result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_SELECTED_TEXT);
proto.write(GetSelectedText.FLAGS, flags);
@@ -127,7 +128,7 @@ public class InputConnectionHelper {
proto.write(GetSelectedText.RESULT, result.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -139,16 +140,16 @@ public class InputConnectionHelper {
* @param flags Supplies additional options controlling how the text is
* returned. May be either {@code 0} or
* {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
- * @param result an {@link android.view.inputmethod.SurroundingText} object describing the
+ * @param result an {@link SurroundingText} object describing the
* surrounding text and state of selection, or null if the input connection is no longer valid,
* or the editor can't comply with the request for some reason, or the application does not
* implement this method. The length of the returned text might be less than the sum of
* <var>beforeLength</var> and <var>afterLength</var> .
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0)
- int beforeLength, @IntRange(from = 0) int afterLength, int flags,
- @Nullable SurroundingText result) {
+ @NonNull
+ public static byte[] buildGetSurroundingTextProto(@IntRange(from = 0) int beforeLength,
+ @IntRange(from = 0) int afterLength, int flags, @Nullable SurroundingText result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_SURROUNDING_TEXT);
proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength);
@@ -169,7 +170,7 @@ public class InputConnectionHelper {
proto.end(token_result);
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -180,9 +181,10 @@ public class InputConnectionHelper {
* {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}.
* @param result the caps mode flags that are in effect at the current
* cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) {
+ @NonNull
+ public static byte[] buildGetCursorCapsModeProto(int reqModes, int result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_CURSOR_CAPS_MODE);
proto.write(GetCursorCapsMode.REQ_MODES, reqModes);
@@ -190,7 +192,7 @@ public class InputConnectionHelper {
proto.write(GetCursorCapsMode.RESULT, result);
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -199,17 +201,18 @@ public class InputConnectionHelper {
* data.
*
* @param request Description of how the text should be returned.
- * {@link android.view.inputmethod.ExtractedTextRequest}
+ * {@link ExtractedTextRequest}
* @param flags Additional options to control the client, either {@code 0} or
* {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}.
- * @param result an {@link android.view.inputmethod.ExtractedText}
+ * @param result an {@link ExtractedText}
* object describing the state of the text view and containing the
* extracted text itself, or null if the input connection is no
* longer valid of the editor can't comply with the request for
* some reason.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest
+ @NonNull
+ public static byte[] buildGetExtractedTextProto(@NonNull ExtractedTextRequest
request, int flags, @Nullable ExtractedText result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_EXTRACTED_TEXT);
@@ -226,6 +229,6 @@ public class InputConnectionHelper {
proto.write(GetExtractedText.RESULT, result.text.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
}
diff --git a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
deleted file mode 100644
index 1cf68872e2cf..000000000000
--- a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.Nullable;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.InputChannel;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.view.IInputMethodSession;
-
-/**
- * A utility class to take care of boilerplate code around IPCs.
- *
- * <p>Note: All public methods are designed to be thread-safe.</p>
- */
-public class MultiClientInputMethodPrivilegedOperations {
- private static final String TAG = "MultiClientInputMethodPrivilegedOperations";
-
- private static final class OpsHolder {
- @Nullable
- @GuardedBy("this")
- private IMultiClientInputMethodPrivilegedOperations mPrivOps;
-
- /**
- * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
- *
- * <p>This method can be called only once.</p>
- *
- * @param privOps Binder interface to be set.
- */
- @AnyThread
- public synchronized void set(IMultiClientInputMethodPrivilegedOperations privOps) {
- if (mPrivOps != null) {
- throw new IllegalStateException(
- "IMultiClientInputMethodPrivilegedOperations must be set at most once."
- + " privOps=" + privOps);
- }
- mPrivOps = privOps;
- }
-
- /**
- * A simplified version of {@link android.os.Debug#getCaller()}.
- *
- * @return method name of the caller.
- */
- @AnyThread
- private static String getCallerMethodName() {
- final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- if (callStack.length <= 4) {
- return "<bottom of call stack>";
- }
- return callStack[4].getMethodName();
- }
-
- @AnyThread
- public synchronized void dispose() {
- mPrivOps = null;
- }
-
- @AnyThread
- @Nullable
- public synchronized IMultiClientInputMethodPrivilegedOperations getAndWarnIfNull() {
- if (mPrivOps == null) {
- Log.e(TAG, getCallerMethodName() + " is ignored."
- + " Call it within attachToken() and InputMethodService.onDestroy()");
- }
- return mPrivOps;
- }
- }
- private final OpsHolder mOps = new OpsHolder();
-
- /**
- * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
- *
- * <p>This method can be called only once.</p>
- *
- * @param privOps Binder interface to be set.
- */
- @AnyThread
- public void set(IMultiClientInputMethodPrivilegedOperations privOps) {
- mOps.set(privOps);
- }
-
- /**
- * Disposes internal Binder proxy so that the real Binder object can be garbage collected.
- */
- @AnyThread
- public void dispose() {
- mOps.dispose();
- }
-
- /**
-
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#createInputMethodWindowToken(int)}.
- *
- * @param displayId display ID on which the IME window will be shown.
- * @return Window token to be specified to the IME window.
- */
- @AnyThread
- public IBinder createInputMethodWindowToken(int displayId) {
- IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return null;
- }
- try {
- return ops.createInputMethodWindowToken(displayId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link
- * IMultiClientInputMethodPrivilegedOperations#deleteInputMethodWindowToken(IBinder)}.
- *
- * @param token Window token that is to be deleted.
- */
- @AnyThread
- public void deleteInputMethodWindowToken(IBinder token) {
- IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.deleteInputMethodWindowToken(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
- * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
- *
- * @param clientId client ID to be accepted.
- * @param session {@link IInputMethodSession} that is also used for traditional IME protocol.
- * @param multiClientSession {@link IMultiClientInputMethodSession} that defines new callbacks
- * for multi-client scenarios.
- * @param writeChannel {@link InputChannel} that is also used for traditional IME protocol.
- */
- @AnyThread
- public void acceptClient(int clientId, IInputMethodSession session,
- IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.acceptClient(clientId, session, multiClientSession, writeChannel);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#reportImeWindowTarget(int, int,
- * IBinder)}.
- *
- * @param clientId client ID about which new IME target window is reported.
- * @param targetWindowHandle integer handle of the target window.
- * @param imeWindowToken {@link IBinder} window token of the IME window.
- */
- @AnyThread
- public void reportImeWindowTarget(int clientId, int targetWindowHandle,
- IBinder imeWindowToken) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#isUidAllowedOnDisplay(int, int)}.
- *
- * @param displayId display ID to be verified.
- * @param uid UID to be verified.
- * @return {@code true} when {@code uid} is allowed to access to {@code displayId}.
- */
- @AnyThread
- public boolean isUidAllowedOnDisplay(int displayId, int uid) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return false;
- }
- try {
- return ops.isUidAllowedOnDisplay(displayId, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#setActive(int, boolean)}.
- * @param clientId client ID to be set active/inactive
- * @param active {@code true} set set active.
- */
- @AnyThread
- public void setActive(int clientId, boolean active) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.setActive(clientId, active);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8c63f38494ea..84ef5a19772e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5099,46 +5099,48 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void notePowerSaveModeLocked(boolean enabled) {
- notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), false);
+ notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
}
/**
- * Handles power save mode state changes.
+ * Toggles the power save mode state.
*/
- public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs,
- boolean forceLog) {
+ public void notePowerSaveModeLockedInit(boolean enabled, long elapsedRealtimeMs,
+ long uptimeMs) {
+ if (mPowerSaveModeEnabled != enabled) {
+ notePowerSaveModeLocked(enabled, elapsedRealtimeMs, uptimeMs);
+ } else {
+ // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
+ // allow the atom to read all future state changes.
+ FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
+ enabled
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ }
+ }
+
+ public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
- mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
- mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Power save mode enabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Power save mode disabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
- } else if (forceLog) {
- // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
- // allow the atom to read all future state changes.
- FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
- enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e4e28a926ed6..92d5a47a2ed0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -747,7 +747,7 @@ public final class Zygote {
new DataOutputStream(sessionSocket.getOutputStream());
Credentials peerCredentials = sessionSocket.getPeerCredentials();
tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket);
- args = ZygoteArguments.getInstance(argBuffer);
+ args = ZygoteArguments.getInstance(tmpArgBuffer);
applyUidSecurityPolicy(args, peerCredentials);
// TODO (chriswailes): Should this only be run for debug builds?
validateUsapCommand(args);
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 60a8d802861f..d3224b13e312 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -16,16 +16,20 @@
package com.android.internal.policy;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -34,12 +38,18 @@ import android.content.res.Configuration;
import android.content.res.ResourceId;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Picture;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.SystemProperties;
import android.util.Slog;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -56,11 +66,17 @@ import java.util.List;
/** @hide */
public class TransitionAnimation {
+ public static final int WALLPAPER_TRANSITION_NONE = 0;
+ public static final int WALLPAPER_TRANSITION_OPEN = 1;
+ public static final int WALLPAPER_TRANSITION_CLOSE = 2;
+ public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3;
+ public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4;
+
// These are the possible states for the enter/exit activities during a thumbnail transition
- public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
- public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
- public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
- public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
/**
* Maximum duration for the clip reveal animation. This is used when there is a lot of movement
@@ -72,9 +88,15 @@ public class TransitionAnimation {
public static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+ /** Fraction of animation at which the recents thumbnail stays completely transparent */
+ private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
+ /** Interpolator to be used for animations that respond directly to a touch */
+ static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
private static final String DEFAULT_PACKAGE = "android";
private final Context mContext;
@@ -86,7 +108,9 @@ public class TransitionAnimation {
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
private final Interpolator mDecelerateInterpolator;
+ private final Interpolator mFastOutLinearInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mThumbnailFadeInInterpolator;
private final Interpolator mThumbnailFadeOutInterpolator;
private final Rect mTmpFromClipRect = new Rect();
private final Rect mTmpToClipRect = new Rect();
@@ -107,8 +131,19 @@ public class TransitionAnimation {
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.decelerate_cubic);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
+ mThumbnailFadeInInterpolator = input -> {
+ // Linear response for first fraction, then complete after that.
+ if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
+ return 0f;
+ }
+ float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION)
+ / (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
+ return mFastOutLinearInInterpolator.getInterpolation(t);
+ };
mThumbnailFadeOutInterpolator = input -> {
// Linear response for first fraction, then complete after that.
if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
@@ -181,6 +216,13 @@ public class TransitionAnimation {
DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
}
+ @Nullable
+ public Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
+ final Animation animation = loadCrossProfileAppThumbnailEnterAnimation();
+ return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
+ appRect.height(), 0, null);
+ }
+
/** Load animation by resource Id from specific package. */
@Nullable
public Animation loadAnimationRes(String packageName, int resId) {
@@ -347,8 +389,15 @@ public class TransitionAnimation {
}
}
- public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
- Rect displayFrame, Rect startRect) {
+ public Animation createClipRevealAnimationLocked(@TransitionType int transit,
+ int wallpaperTransit, boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
+ return createClipRevealAnimationLockedCompat(
+ getTransitCompatType(transit, wallpaperTransit), enter, appFrame, displayFrame,
+ startRect);
+ }
+
+ public Animation createClipRevealAnimationLockedCompat(@TransitionOldType int transit,
+ boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
final Animation anim;
if (enter) {
final int appWidth = appFrame.width();
@@ -458,8 +507,14 @@ public class TransitionAnimation {
return anim;
}
- public Animation createScaleUpAnimationLocked(int transit, boolean enter,
- Rect containingFrame, Rect startRect) {
+ public Animation createScaleUpAnimationLocked(@TransitionType int transit, int wallpaperTransit,
+ boolean enter, Rect containingFrame, Rect startRect) {
+ return createScaleUpAnimationLockedCompat(getTransitCompatType(transit, wallpaperTransit),
+ enter, containingFrame, startRect);
+ }
+
+ public Animation createScaleUpAnimationLockedCompat(@TransitionOldType int transit,
+ boolean enter, Rect containingFrame, Rect startRect) {
Animation a;
setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
final int appWidth = containingFrame.width();
@@ -514,12 +569,19 @@ public class TransitionAnimation {
return a;
}
+ public Animation createThumbnailEnterExitAnimationLocked(boolean enter, boolean scaleUp,
+ Rect containingFrame, @TransitionType int transit, int wallpaperTransit,
+ HardwareBuffer thumbnailHeader, Rect startRect) {
+ return createThumbnailEnterExitAnimationLockedCompat(enter, scaleUp, containingFrame,
+ getTransitCompatType(transit, wallpaperTransit), thumbnailHeader, startRect);
+ }
+
/**
* This animation is created when we are doing a thumbnail transition, for the activity that is
* leaving, and the activity that is entering.
*/
- public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState,
- Rect containingFrame, int transit, HardwareBuffer thumbnailHeader,
+ public Animation createThumbnailEnterExitAnimationLockedCompat(boolean enter, boolean scaleUp,
+ Rect containingFrame, @TransitionOldType int transit, HardwareBuffer thumbnailHeader,
Rect startRect) {
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
@@ -529,6 +591,7 @@ public class TransitionAnimation {
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+ final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
@@ -587,8 +650,8 @@ public class TransitionAnimation {
* This alternate animation is created when we are doing a thumbnail transition, for the
* activity that is leaving, and the activity that is entering.
*/
- public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ public Animation createAspectScaledThumbnailEnterExitAnimationLocked(boolean enter,
+ boolean scaleUp, int orientation, int transit, Rect containingFrame, Rect contentInsets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
Rect startRect, Rect defaultStartRect) {
Animation a;
@@ -601,11 +664,11 @@ public class TransitionAnimation {
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
final int thumbStartY = mTmpRect.top - containingFrame.top;
+ final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
if (freeform && scaleUp) {
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, startRect, defaultStartRect);
@@ -720,10 +783,151 @@ public class TransitionAnimation {
}
/**
+ * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+ * when a thumbnail is specified with the pending animation override.
+ */
+ public Animation createThumbnailAspectScaleAnimationLocked(Rect appRect,
+ @Nullable Rect contentInsets, HardwareBuffer thumbnailHeader, int orientation,
+ Rect startRect, Rect defaultStartRect, boolean scaleUp) {
+ Animation a;
+ final int thumbWidthI = thumbnailHeader.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = thumbnailHeader.getHeight();
+ final int appWidth = appRect.width();
+
+ float scaleW = appWidth / thumbWidth;
+ getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
+ final float fromX;
+ float fromY;
+ final float toX;
+ float toY;
+ final float pivotX;
+ final float pivotY;
+ if (shouldScaleDownThumbnailTransition(orientation)) {
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+
+ // For the curved translate animation to work, the pivot points needs to be at the
+ // same absolute position as the one from the real surface.
+ toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
+ toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
+ pivotX = mTmpRect.width() / 2;
+ pivotY = appRect.height() / 2 / scaleW;
+ if (mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header is displayed above the thumbnail instead of
+ // overlapping it.
+ fromY -= thumbHeightI;
+ toY -= thumbHeightI * scaleW;
+ }
+ } else {
+ pivotX = 0;
+ pivotY = 0;
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+ toX = appRect.left;
+ toY = appRect.top;
+ }
+ if (scaleUp) {
+ // Animation up from the thumbnail to the full screen
+ Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
+ scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation alpha = new AlphaAnimation(1f, 0f);
+ alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+ alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
+ translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
+ mTmpToClipRect.set(appRect);
+
+ // Containing frame is in screen space, but we need the clip rect in the
+ // app space.
+ mTmpToClipRect.offsetTo(0, 0);
+ mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+ mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
+
+ if (contentInsets != null) {
+ mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
+ (int) (-contentInsets.top * scaleW),
+ (int) (-contentInsets.right * scaleW),
+ (int) (-contentInsets.bottom * scaleW));
+ }
+
+ Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+ clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ if (!mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header should be shown for the whole animation.
+ set.addAnimation(alpha);
+ }
+ set.addAnimation(translate);
+ set.addAnimation(clipAnim);
+ a = set;
+ } else {
+ // Animation down from the full screen to the thumbnail
+ Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
+ scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation alpha = new AlphaAnimation(0f, 1f);
+ alpha.setInterpolator(mThumbnailFadeInInterpolator);
+ alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
+ translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ if (!mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header should be shown for the whole animation.
+ set.addAnimation(alpha);
+ }
+ set.addAnimation(translate);
+ a = set;
+
+ }
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
+ null);
+ }
+
+ /**
+ * Creates an overlay with a background color and a thumbnail for the cross profile apps
+ * animation.
+ */
+ public HardwareBuffer createCrossProfileAppsThumbnail(
+ @DrawableRes int thumbnailDrawableRes, Rect frame) {
+ final int width = frame.width();
+ final int height = frame.height();
+
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(width, height);
+ canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
+ final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
+ final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
+ drawable.setBounds(
+ (width - thumbnailSize) / 2,
+ (height - thumbnailSize) / 2,
+ (width + thumbnailSize) / 2,
+ (height + thumbnailSize) / 2);
+ drawable.setTint(mContext.getColor(android.R.color.white));
+ drawable.draw(canvas);
+ picture.endRecording();
+
+ return Bitmap.createBitmap(picture).getHardwareBuffer();
+ }
+
+ /**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight,
- int transit) {
+ @TransitionOldType int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
@@ -820,6 +1024,22 @@ public class TransitionAnimation {
return anim;
}
+ private static @TransitionOldType int getTransitCompatType(@TransitionType int transit,
+ int wallpaperTransit) {
+ if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+ } else if (transit == TRANSIT_OPEN) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ } else if (transit == TRANSIT_CLOSE) {
+ return TRANSIT_OLD_ACTIVITY_CLOSE;
+ }
+
+ // We only do some special handle for above type, so use type NONE for default behavior.
+ return TRANSIT_OLD_NONE;
+ }
+
/**
* Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
* the start rect is outside of the target rect, and there is a lot of movement going on.
@@ -843,10 +1063,33 @@ public class TransitionAnimation {
}
/**
+ * Return the current thumbnail transition state.
+ */
+ private int getThumbnailTransitionState(boolean enter, boolean scaleUp) {
+ if (enter) {
+ if (scaleUp) {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+ }
+ } else {
+ if (scaleUp) {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+ }
+ }
+ }
+
+ /**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
+ public static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
int appHeight, long duration, Interpolator interpolator) {
+ if (a == null) {
+ return null;
+ }
+
if (duration > 0) {
a.setDuration(duration);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 10f14b42ae42..4f940dbb1d7a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -25,6 +25,7 @@ import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.service.notification.StatusBarNotification;
+import android.view.InsetsState;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
@@ -182,7 +183,7 @@ oneway interface IStatusBar
/**
* Notifies System UI side of system bar attribute change on the specified display.
*
- * @param displayId the ID of the display to notify
+ * @param displayId the ID of the display to notify.
* @param appearance the appearance of the focused window. The light top bar appearance is not
* controlled here, but primaryAppearance and secondaryAppearance.
* @param appearanceRegions a set of appearances which will be only applied in their own bounds.
@@ -191,11 +192,12 @@ oneway interface IStatusBar
* stacks.
* @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
* @param behavior the behavior of the focused window.
- * @param isFullscreen whether any of status or navigation bar is requested invisible.
+ * @param requestedState the collection of the requested visibilities of system insets.
+ * @param packageName the package name of the focused app.
*/
void onSystemBarAttributesChanged(int displayId, int appearance,
in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- int behavior, boolean isFullscreen);
+ int behavior, in InsetsState requestedVisibilities, String packageName);
/**
* Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 8fb2f9cd8bf9..2fd1691e5e02 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -21,6 +21,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.view.InsetsState;
import com.android.internal.view.AppearanceRegion;
@@ -39,14 +40,15 @@ public final class RegisterStatusBarResult implements Parcelable {
public final IBinder mImeToken;
public final boolean mNavbarColorManagedByIme;
public final int mBehavior;
- public final boolean mAppFullscreen;
+ public final InsetsState mRequestedState;
+ public final String mPackageName;
public final int[] mTransientBarTypes;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
- boolean navbarColorManagedByIme, int behavior, boolean appFullscreen,
- @NonNull int[] transientBarTypes) {
+ boolean navbarColorManagedByIme, int behavior, InsetsState requestedState,
+ String packageName, @NonNull int[] transientBarTypes) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
@@ -58,7 +60,8 @@ public final class RegisterStatusBarResult implements Parcelable {
mImeToken = imeToken;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
- mAppFullscreen = appFullscreen;
+ mRequestedState = requestedState;
+ mPackageName = packageName;
mTransientBarTypes = transientBarTypes;
}
@@ -80,7 +83,8 @@ public final class RegisterStatusBarResult implements Parcelable {
dest.writeStrongBinder(mImeToken);
dest.writeBoolean(mNavbarColorManagedByIme);
dest.writeInt(mBehavior);
- dest.writeBoolean(mAppFullscreen);
+ dest.writeTypedObject(mRequestedState, 0);
+ dest.writeString(mPackageName);
dest.writeIntArray(mTransientBarTypes);
}
@@ -104,12 +108,13 @@ public final class RegisterStatusBarResult implements Parcelable {
final IBinder imeToken = source.readStrongBinder();
final boolean navbarColorManagedByIme = source.readBoolean();
final int behavior = source.readInt();
- final boolean appFullscreen = source.readBoolean();
+ final InsetsState requestedState = source.readTypedObject(InsetsState.CREATOR);
+ final String packageName = source.readString();
final int[] transientBarTypes = source.createIntArray();
return new RegisterStatusBarResult(icons, disabledFlags1, appearance,
appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher,
disabledFlags2, imeToken, navbarColorManagedByIme, behavior,
- appFullscreen, transientBarTypes);
+ requestedState, packageName, transientBarTypes);
}
@Override
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index a0454510b30b..5b68159888cc 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,5 +1,6 @@
per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
per-file Protocol* = etancohen@google.com, lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file DataClass* = eugenesusla@google.com \ No newline at end of file
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 783d088b19b9..6d1b4cb2d49f 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -27,8 +27,6 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
-import android.util.imetracing.ImeTracing;
-import android.util.imetracing.InputConnectionHelper;
import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
import android.view.View;
@@ -49,6 +47,8 @@ import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
import com.android.internal.inputmethod.ISurroundingTextResultCallback;
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputConnectionProtoDumper;
import com.android.internal.os.SomeArgs;
import java.lang.ref.WeakReference;
@@ -350,7 +350,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
}
void executeMessage(Message msg) {
- ProtoOutputStream icProto;
+ byte[] icProto;
switch (msg.what) {
case DO_GET_TEXT_AFTER_CURSOR: {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
@@ -366,7 +366,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
}
if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
+ icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(msg.arg1,
msg.arg2, result);
ImeTracing.getInstance().triggerClientDump(
TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
@@ -396,7 +396,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
}
if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
+ icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(msg.arg1,
msg.arg2, result);
ImeTracing.getInstance().triggerClientDump(
TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
@@ -426,7 +426,8 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
result = ic.getSelectedText(msg.arg1);
}
if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
+ icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(msg.arg1,
+ result);
ImeTracing.getInstance().triggerClientDump(
TAG + "#getSelectedText", mParentInputMethodManager, icProto);
}
@@ -459,8 +460,8 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
result = ic.getSurroundingText(beforeLength, afterLength, flags);
}
if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
- afterLength, flags, result);
+ icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
+ beforeLength, afterLength, flags, result);
ImeTracing.getInstance().triggerClientDump(
TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
}
@@ -489,7 +490,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
result = ic.getCursorCapsMode(msg.arg1);
}
if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
+ icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(msg.arg1,
result);
ImeTracing.getInstance().triggerClientDump(
TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
@@ -521,7 +522,7 @@ public final class IInputConnectionWrapper extends IInputContext.Stub {
result = ic.getExtractedText(request, msg.arg1);
}
if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
+ icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(request,
msg.arg1, result);
ImeTracing.getInstance().triggerClientDump(
TAG + "#getExtractedText", mParentInputMethodManager, icProto);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8d82e33dc29f..5354afbd667b 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,7 +35,7 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
* {@hide}
*/
oneway interface IInputMethod {
- void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
+ void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
int configChanges);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index df371ce11954..92105b97e00a 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -53,8 +53,7 @@ public final class InputBindResult implements Parcelable {
ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
ResultCode.ERROR_NO_EDITOR,
ResultCode.ERROR_DISPLAY_ID_MISMATCH,
- ResultCode.ERROR_INVALID_DISPLAY_ID,
- ResultCode.ERROR_INVALID_CLIENT,
+ ResultCode.ERROR_INVALID_DISPLAY_ID
})
public @interface ResultCode {
/**
@@ -168,10 +167,6 @@ public final class InputBindResult implements Parcelable {
* display.
*/
int ERROR_INVALID_DISPLAY_ID = 15;
- /**
- * Indicates that the client is not recognized by the system.
- */
- int ERROR_INVALID_CLIENT = 16;
}
@ResultCode
@@ -310,8 +305,6 @@ public final class InputBindResult implements Parcelable {
return "ERROR_DISPLAY_ID_MISMATCH";
case ResultCode.ERROR_INVALID_DISPLAY_ID:
return "ERROR_INVALID_DISPLAY_ID";
- case ResultCode.ERROR_INVALID_CLIENT:
- return "ERROR_INVALID_CLIENT";
default:
return "Unknown(" + result + ")";
}
@@ -370,11 +363,6 @@ public final class InputBindResult implements Parcelable {
error(ResultCode.ERROR_INVALID_DISPLAY_ID);
/**
- * Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
- */
- public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
-
- /**
* Predefined <strong>success</strong> object for
* {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
*/
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 91a19e087bf1..1d07c26de17a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -230,6 +230,7 @@ cc_library_shared {
"libbinderthreadstateutils",
"libdmabufinfo",
"libgif",
+ "libgui_window_info_static",
"libseccomp_policy",
"libgrallocusage",
"libscrypt_static",
@@ -369,6 +370,7 @@ cc_library_shared {
"libinput",
"libbinderthreadstateutils",
"libsqlite",
+ "libgui_window_info_static",
],
shared_libs: [
// libbinder needs to be shared since it has global state
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
index ec99d6da5b8e..7eb7ac4bc305 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -19,7 +19,7 @@
#include <string>
-#include <input/InputApplication.h>
+#include <gui/InputApplication.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 463d909821b1..3a095621272a 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -26,14 +26,17 @@
#include <ui/Region.h>
#include <utils/threads.h>
+#include <gui/WindowInfo.h>
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
-#include "input/InputWindow.h"
#include "jni.h"
namespace android {
+using gui::TouchOcclusionMode;
+using gui::WindowInfo;
+
struct WeakRefHandleField {
jfieldID ctrl;
jmethodID get;
@@ -66,7 +69,6 @@ static struct {
jfieldID packageName;
jfieldID inputFeatures;
jfieldID displayId;
- jfieldID portalToDisplayId;
jfieldID replaceTouchableRegionWithCrop;
WeakRefHandleField touchableRegionSurfaceControl;
} gInputWindowHandleClassInfo;
@@ -115,9 +117,9 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
- mInfo.flags = Flags<InputWindowInfo::Flag>(
+ mInfo.flags = Flags<WindowInfo::Flag>(
env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
- mInfo.type = static_cast<InputWindowInfo::Type>(
+ mInfo.type = static_cast<WindowInfo::Type>(
env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
mInfo.dispatchingTimeout = std::chrono::milliseconds(
env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
@@ -159,12 +161,10 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.ownerUid = env->GetIntField(obj,
gInputWindowHandleClassInfo.ownerUid);
mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
- mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>(
+ mInfo.inputFeatures = static_cast<WindowInfo::Feature>(
env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
- mInfo.portalToDisplayId = env->GetIntField(obj,
- gInputWindowHandleClassInfo.portalToDisplayId);
jobject inputApplicationHandleObj = env->GetObjectField(obj,
gInputWindowHandleClassInfo.inputApplicationHandle);
@@ -348,9 +348,6 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
"displayId", "I");
- GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz,
- "portalToDisplayId", "I");
-
GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz,
"replaceTouchableRegionWithCrop", "Z");
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index de5bd6ef97f4..635480fc5abc 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -17,14 +17,14 @@
#ifndef _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
#define _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
namespace android {
-class NativeInputWindowHandle : public InputWindowHandle {
+class NativeInputWindowHandle : public gui::WindowInfoHandle {
public:
NativeInputWindowHandle(jweak objWeak);
virtual ~NativeInputWindowHandle();
@@ -37,7 +37,6 @@ private:
jweak mObjWeak;
};
-
extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
JNIEnv* env, jobject inputWindowHandleObj);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 6e2b9cf250c6..9f0e71d100bb 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -28,6 +28,7 @@
#include <android/media/AudioVibratorInfo.h>
#include <audiomanager/AudioManager.h>
+#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
#include <media/MicrophoneInfo.h>
@@ -207,6 +208,7 @@ static struct {
jmethodID getId;
jmethodID getResonantFrequency;
jmethodID getQFactor;
+ jmethodID getMaxAmplitude;
} gVibratorMethods;
static Mutex gLock;
@@ -812,7 +814,8 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz)
static jint
android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
{
- return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
+ return (jint)deviceTypesToBitMask(
+ AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(stream)));
}
static jint
@@ -2677,6 +2680,8 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz
vibratorInfo.resonantFrequency =
env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getResonantFrequency);
vibratorInfo.qFactor = env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getQFactor);
+ vibratorInfo.maxAmplitude =
+ env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getMaxAmplitude);
vibratorInfos.push_back(vibratorInfo);
}
return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos));
@@ -3041,6 +3046,8 @@ int register_android_media_AudioSystem(JNIEnv *env)
gVibratorMethods.getResonantFrequency =
GetMethodIDOrDie(env, vibratorClass, "getResonantFrequency", "()F");
gVibratorMethods.getQFactor = GetMethodIDOrDie(env, vibratorClass, "getQFactor", "()F");
+ gVibratorMethods.getMaxAmplitude =
+ GetMethodIDOrDie(env, vibratorClass, "getHapticChannelMaximumAmplitude", "()F");
AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b40491a49b14..f44e829d49d7 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -50,8 +50,7 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
}
void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName,
- jstring devOptIn, jobjectArray featuresObj, jobject rulesFd,
- jlong rulesOffset, jlong rulesLength) {
+ jstring devOptIn, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars appNameChars(env, appName);
ScopedUtfChars devOptInChars(env, devOptIn);
@@ -74,11 +73,8 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa
}
}
- int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
-
android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
- devOptInChars.c_str(), features,
- rulesFd_native, rulesOffset, rulesLength);
+ devOptInChars.c_str(), features);
}
bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) {
@@ -124,8 +120,7 @@ const JNINativeMethod g_methods[] = {
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
{"setAngleInfo",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/io/"
- "FileDescriptor;JJ)V",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
{"getShouldUseAngle", "(Ljava/lang/String;)Z",
reinterpret_cast<void*>(shouldUseAngle_native)},
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 45e3d1b97e83..16366a4e5bec 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -155,6 +155,7 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent
event->getYPrecision(),
event->getRawXCursorPosition(),
event->getRawYCursorPosition(),
+ event->getDisplayOrientation(),
event->getDisplaySize().x,
event->getDisplaySize().y, event->getDownTime(),
event->getHistoricalEventTime(i),
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 6971301cec32..cabf3abe350a 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -22,6 +22,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
#include <input/Input.h>
#include <nativehelper/ScopedUtfChars.h>
#include <utils/Log.h>
@@ -56,6 +57,8 @@ static struct {
jfieldID toolMajor;
jfieldID toolMinor;
jfieldID orientation;
+ jfieldID relativeX;
+ jfieldID relativeY;
} gPointerCoordsClassInfo;
static struct {
@@ -212,6 +215,12 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+ outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
+ env->GetFloatField(pointerCoordsObj,
+ gPointerCoordsClassInfo.relativeX));
+ outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
+ env->GetFloatField(pointerCoordsObj,
+ gPointerCoordsClassInfo.relativeY));
BitSet64 bits =
BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
@@ -261,6 +270,12 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
float rawY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y);
vec2 transformed = transform.transform(rawX, rawY);
+ float rawRelX = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ float rawRelY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ // Apply only rotation and scale, not translation.
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ const vec2 transformedRel = transform.transform(rawRelX, rawRelY) - transformedOrigin;
+
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, transformed.x);
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, transformed.y);
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure,
@@ -277,6 +292,8 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR));
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeX, transformedRel.x);
+ env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeY, transformedRel.y);
uint64_t outBits = 0;
BitSet64 bits = BitSet64(rawPointerCoords->bits);
@@ -289,6 +306,8 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
+ bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_X);
+ bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_Y);
if (!bits.isEmpty()) {
uint32_t packedAxesCount = bits.count();
jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
@@ -378,8 +397,8 @@ static jlong android_view_MotionEvent_nativeInitialize(
flags, edgeFlags, metaState, buttonState,
static_cast<MotionClassification>(classification), transform, xPrecision,
yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
- AMOTION_EVENT_INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+ INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos,
pointerCount, pointerProperties, rawPointerCoords);
return reinterpret_cast<jlong>(event.release());
@@ -872,6 +891,8 @@ int register_android_view_MotionEvent(JNIEnv* env) {
gPointerCoordsClassInfo.toolMajor = GetFieldIDOrDie(env, clazz, "toolMajor", "F");
gPointerCoordsClassInfo.toolMinor = GetFieldIDOrDie(env, clazz, "toolMinor", "F");
gPointerCoordsClassInfo.orientation = GetFieldIDOrDie(env, clazz, "orientation", "F");
+ gPointerCoordsClassInfo.relativeX = GetFieldIDOrDie(env, clazz, "relativeX", "F");
+ gPointerCoordsClassInfo.relativeY = GetFieldIDOrDie(env, clazz, "relativeY", "F");
clazz = FindClassOrDie(env, "android/view/MotionEvent$PointerProperties");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1695e1af9270..1955ec590737 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -62,6 +62,8 @@
namespace android {
+using gui::FocusRequest;
+
static void doThrowNPE(JNIEnv* env) {
jniThrowNullPointerException(env, NULL);
}
@@ -882,8 +884,9 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
}
static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
- sp<IBinder> token =
- SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
+ if (!id) return nullptr;
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
return javaObjectForIBinder(env, token);
}
@@ -1006,6 +1009,17 @@ static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz,
}
}
+static void nativeSetDisplayFlags(JNIEnv* env, jclass clazz, jlong transactionObj, jobject tokenObj,
+ jint flags) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+
+ {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ transaction->setDisplayFlags(token, flags);
+ }
+}
+
static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
jlong transactionObj,
jobject tokenObj, jint orientation,
@@ -1881,6 +1895,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
(void*)nativeSetDisplayLayerStack },
+ {"nativeSetDisplayFlags", "(JLandroid/os/IBinder;I)V",
+ (void*)nativeSetDisplayFlags },
{"nativeSetDisplayProjection", "(JLandroid/os/IBinder;IIIIIIIII)V",
(void*)nativeSetDisplayProjection },
{"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
diff --git a/core/proto/android/server/OWNERS b/core/proto/android/server/OWNERS
new file mode 100644
index 000000000000..72d39bfb3cd6
--- /dev/null
+++ b/core/proto/android/server/OWNERS
@@ -0,0 +1 @@
+per-file window*.proto = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto
index 1fc4a01936b1..41fecfd6cdb7 100644
--- a/core/proto/android/server/accessibilitytrace.proto
+++ b/core/proto/android/server/accessibilitytrace.proto
@@ -46,17 +46,17 @@ message AccessibilityTraceProto {
/* required: elapsed realtime in nanos since boot of when this entry was logged */
optional fixed64 elapsed_realtime_nanos = 1;
optional string calendar_time = 2;
-
- optional string process_name = 3;
- optional string thread_id_name = 4;
+ repeated string logging_type = 3;
+ optional string process_name = 4;
+ optional string thread_id_name = 5;
/* where the trace originated */
- optional string where = 5;
+ optional string where = 6;
- optional string calling_pkg = 6;
- optional string calling_params = 7;
- optional string calling_stacks = 8;
+ optional string calling_pkg = 7;
+ optional string calling_params = 8;
+ optional string calling_stacks = 9;
- optional AccessibilityDumpProto accessibility_service = 9;
- optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10;
+ optional AccessibilityDumpProto accessibility_service = 10;
+ optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 11;
}
diff --git a/core/proto/android/server/inputmethod/OWNERS b/core/proto/android/server/inputmethod/OWNERS
new file mode 100644
index 000000000000..5deb2ce8f24b
--- /dev/null
+++ b/core/proto/android/server/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index fa1e9d4afcdf..0121bff3e7ef 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -278,7 +278,7 @@ message PinnedTaskControllerProto {
message TaskProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional WindowContainerProto window_container = 1;
+ optional WindowContainerProto window_container = 1 [deprecated=true];
optional int32 id = 2;
reserved 3; // activity
optional bool fills_parent = 4;
@@ -295,12 +295,12 @@ message TaskProto {
optional string real_activity = 13;
optional string orig_activity = 14;
- optional int32 display_id = 15;
+ optional int32 display_id = 15 [deprecated=true];
optional int32 root_task_id = 16;
- optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+ optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType", deprecated=true] ;
optional int32 resize_mode = 18 [(.android.typedef) = "android.appwidget.AppWidgetProviderInfo.ResizeModeFlags"];
- optional int32 min_width = 19;
- optional int32 min_height = 20;
+ optional int32 min_width = 19 [deprecated=true];
+ optional int32 min_height = 20 [deprecated=true];
optional .android.graphics.RectProto adjusted_bounds = 21;
optional .android.graphics.RectProto last_non_fullscreen_bounds = 22;
@@ -312,6 +312,18 @@ message TaskProto {
optional bool created_by_organizer = 28;
optional string affinity = 29;
optional bool has_child_pip_activity = 30;
+ optional TaskFragmentProto task_fragment = 31;
+}
+
+/* represents TaskFragment */
+message TaskFragmentProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional WindowContainerProto window_container = 1;
+ optional int32 display_id = 2;
+ optional int32 activity_type = 3 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+ optional int32 min_width = 4;
+ optional int32 min_height = 5;
}
/* represents ActivityRecordProto */
@@ -493,6 +505,8 @@ message WindowContainerChildProto {
optional WindowTokenProto window_token = 7;
/* represents a WindowState child */
optional WindowStateProto window = 8;
+ /* represents a WindowState child */
+ optional TaskFragmentProto task_fragment = 9;
}
/* represents ConfigurationContainer */
diff --git a/core/proto/android/view/OWNERS b/core/proto/android/view/OWNERS
new file mode 100644
index 000000000000..d72a0f00c752
--- /dev/null
+++ b/core/proto/android/view/OWNERS
@@ -0,0 +1,3 @@
+include /services/core/java/com/android/server/wm/OWNERS
+
+per-file ime*.proto = file:/core/java/android/view/inputmethod/OWNERS
diff --git a/core/proto/android/view/inputmethod/OWNERS b/core/proto/android/view/inputmethod/OWNERS
new file mode 100644
index 000000000000..5deb2ce8f24b
--- /dev/null
+++ b/core/proto/android/view/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4dec0ffccb5e..c6317ccf96f3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -202,6 +202,8 @@
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
@@ -2585,7 +2587,7 @@
third-party apps.
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|role" />
<!-- Allows an application to manage access to crates, usually as part
of a crates picker.
@@ -2602,7 +2604,7 @@
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide
Allows an application to aggressively allocate disk space.
@@ -2748,7 +2750,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature|documenter|recents" />
+ android:protectionLevel="signature|recents|role" />
<!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead.
@SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 614779a79253..c6983ae5e045 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -93,7 +93,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 2991b1706a64..95ddc2e4ea79 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -93,7 +93,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index 3564f9755a5d..bef1d0b319b4 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -119,7 +119,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 4920c7953d65..7b834a4eca11 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -21,7 +21,7 @@
android:layout_height="@dimen/notification_text_height"
android:layout_gravity="top"
android:layout_marginTop="@dimen/notification_text_margin_top"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:gravity="top"
android:singleLine="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 6946b3c07652..56d9a84f93f1 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie gemerk nie"</string>
<string name="selected" msgid="6614607926197755875">"gekies"</string>
<string name="not_selected" msgid="410652016565864475">"nie gekies nie"</string>
+ <string name="in_progress" msgid="2149208189184319441">"aan die gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Voltooi handeling met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Voltooi handeling met gebruik van %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Voltooi handeling"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9ab2f1939c84..39a3d6e02b99 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
<string name="selected" msgid="6614607926197755875">"ተመርጧል"</string>
<string name="not_selected" msgid="410652016565864475">"አልተመረጠም"</string>
+ <string name="in_progress" msgid="2149208189184319441">"በሂደት ላይ"</string>
<string name="whichApplication" msgid="5432266899591255759">"... በመጠቀም ድርጊቱን አጠናቅ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sን ተጠቅመው እርምጃ ያጠናቅቁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"እርምጃውን አጠናቅቅ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cf2f677dfb35..7bcd93e9c86e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1262,6 +1262,7 @@
<string name="not_checked" msgid="7972320087569023342">"لم يتم وضع علامة"</string>
<string name="selected" msgid="6614607926197755875">"محدّد"</string>
<string name="not_selected" msgid="410652016565864475">"غير محدّد"</string>
+ <string name="in_progress" msgid="2149208189184319441">"قيد التقدّم"</string>
<string name="whichApplication" msgid="5432266899591255759">"إكمال الإجراء باستخدام"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏إكمال الإجراء باستخدام %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"إكمال الإجراء"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f05e18fa2ce3..f3fbe00bcd25 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিক চিহ্ন দিয়া হোৱা নাই"</string>
<string name="selected" msgid="6614607926197755875">"বাছনি কৰা"</string>
<string name="not_selected" msgid="410652016565864475">"বাছনি কৰা হোৱা নাই"</string>
+ <string name="in_progress" msgid="2149208189184319441">"চলি আছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"কাৰ্য সম্পূৰ্ণ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index bab05ad489b3..00145ba10009 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"yoxlanılmayıb"</string>
<string name="selected" msgid="6614607926197755875">"seçilib"</string>
<string name="not_selected" msgid="410652016565864475">"seçilməyib"</string>
+ <string name="in_progress" msgid="2149208189184319441">"davam edir"</string>
<string name="whichApplication" msgid="5432266899591255759">"Əməliyyatı tamamlayın:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s istifadə edərək əməliyyatı tamamlayın"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Əməliyyatı tamamlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 4179ab03dac3..65bfec1e9bf2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"izabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije izabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 39688221f30d..d672a64805c5 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"не пазначана"</string>
<string name="selected" msgid="6614607926197755875">"выбраны"</string>
<string name="not_selected" msgid="410652016565864475">"не выбраны"</string>
+ <string name="in_progress" msgid="2149208189184319441">"выконваецца"</string>
<string name="whichApplication" msgid="5432266899591255759">"Завяршыць дзеянне з дапамогай"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завяршыць дзеянне з дапамогай %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завяршыць дзеянне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 40807e5650fa..c74f4889cbc0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"без отметка"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"в ход"</string>
<string name="whichApplication" msgid="5432266899591255759">"Изпълняване на действието чрез"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завършване на действието посредством %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Изпълняване на действието"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 47bcbe189570..006d99d6de6f 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string>
<string name="selected" msgid="6614607926197755875">"বেছে নেওয়া হয়েছে"</string>
<string name="not_selected" msgid="410652016565864475">"বেছে নেওয়া হয়নি"</string>
+ <string name="in_progress" msgid="2149208189184319441">"কাজ চলছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 3ebb7ccc8471..b6497abb33be 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 880ad1804498..507fecd8460a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionat"</string>
<string name="selected" msgid="6614607926197755875">"seleccionat"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionat"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'acció mitjançant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completa l\'acció amb %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa l\'acció"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 61fb4710b20c..2ca6c520d122 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nevybráno"</string>
<string name="selected" msgid="6614607926197755875">"vybráno"</string>
<string name="not_selected" msgid="410652016565864475">"nevybráno"</string>
+ <string name="in_progress" msgid="2149208189184319441">"probíhá"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončit akci pomocí aplikace"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončit akci pomocí aplikace %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončit akci"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 45f02e363fe9..0c26400158f4 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"slået fra"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"i gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Brug"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Gennemfør handling ved hjælp af %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Afslut handling"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 088495e9eee4..06d18168a7fa 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"deaktiviert"</string>
<string name="selected" msgid="6614607926197755875">"ausgewählt"</string>
<string name="not_selected" msgid="410652016565864475">"nicht ausgewählt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"Noch nicht abgeschlossen"</string>
<string name="whichApplication" msgid="5432266899591255759">"Aktion durchführen mit"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Aktion mit %1$s abschließen"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Abschließen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 67bd43325cf7..6fe7d63537c2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"μη επιλεγμένο"</string>
<string name="selected" msgid="6614607926197755875">"επιλεγμένο"</string>
<string name="not_selected" msgid="410652016565864475">"μη επιλεγμένο"</string>
+ <string name="in_progress" msgid="2149208189184319441">"σε εξέλιξη"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ολοκληρωμένη ενέργεια με χρήση %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ολοκλήρωση ενέργειας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 141cf4e60e94..bb26e6cc096a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2b1d3e0708f2..9dd378cee63a 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 01d8b392e90a..81f49ced5d2b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 3ed0279fb8f2..ddbcfb83509a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index a7ad2b6dc16a..996a33244a8d 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎not checked‎‏‎‎‏‎"</string>
<string name="selected" msgid="6614607926197755875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎selected‎‏‎‎‏‎"</string>
<string name="not_selected" msgid="410652016565864475">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎not selected‎‏‎‎‏‎"</string>
+ <string name="in_progress" msgid="2149208189184319441">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎in progress‎‏‎‎‏‎"</string>
<string name="whichApplication" msgid="5432266899591255759">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎Complete action using‎‏‎‎‏‎"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎Complete action using %1$s‎‏‎‎‏‎"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎Complete action‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5206c484b893..569cfcffabf6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 93c7bc1df4d1..1605ff05d8bd 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4dd7d9469f4c..6153a8328174 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"märkimata"</string>
<string name="selected" msgid="6614607926197755875">"valitud"</string>
<string name="not_selected" msgid="410652016565864475">"pole valitud"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pooleli"</string>
<string name="whichApplication" msgid="5432266899591255759">"Lõpetage toiming rakendusega"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Toimingu lõpetamine, kasutades rakendust %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Vii toiming lõpule"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d4705c100a8f..a56d19aaa701 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"markatu gabe"</string>
<string name="selected" msgid="6614607926197755875">"hautatuta"</string>
<string name="not_selected" msgid="410652016565864475">"hautatu gabe"</string>
+ <string name="in_progress" msgid="2149208189184319441">"abian"</string>
<string name="whichApplication" msgid="5432266899591255759">"Gauzatu ekintza hau erabilita:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Osatu ekintza %1$s erabiliz"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Osatu ekintza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1b5e4b979fff..5bdc67287e22 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
<string name="selected" msgid="6614607926197755875">"انتخاب شده"</string>
<string name="not_selected" msgid="410652016565864475">"انتخاب نشده"</string>
+ <string name="in_progress" msgid="2149208189184319441">"درحال انجام"</string>
<string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5998269aae48..5c7b4a736133 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ei valittu"</string>
<string name="selected" msgid="6614607926197755875">"valittu"</string>
<string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
+ <string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
<string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 860e10bcff4d..83a9ac21912d 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"non coché"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 2f8deaf97111..561e52cf5f73 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 141da9c1c719..66fcf5b7d3ba 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"non seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"elemento seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"elemento non seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar a acción usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar a acción usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index f51a9a5c8fe0..b6deca511701 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string>
<string name="selected" msgid="6614607926197755875">"પસંદ કરેલી"</string>
<string name="not_selected" msgid="410652016565864475">"પસંદ નહીં કરેલી"</string>
+ <string name="in_progress" msgid="2149208189184319441">"પ્રક્રિયા ચાલુ છે"</string>
<string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ક્રિયા પૂર્ણ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index dcf357b2307a..b5261e03b62f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"बंद है"</string>
<string name="selected" msgid="6614607926197755875">"चुना गया"</string>
<string name="not_selected" msgid="410652016565864475">"नहीं चुना गया"</string>
+ <string name="in_progress" msgid="2149208189184319441">"जारी है"</string>
<string name="whichApplication" msgid="5432266899591255759">"इसका इस्तेमाल करके कार्रवाई को पूरा करें"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s का उपयोग करके कार्रवाई पूरी करें"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"कार्रवाई पूरी करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 9aa3350d6ecd..281cf8504280 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije potvrđeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u tijeku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Radnju dovrši pomoću stavke"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršavanje radnje pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dovrši radnju"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 908b77956b44..9e0232da0100 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"nincs kiválasztva"</string>
<string name="selected" msgid="6614607926197755875">"kiválasztva"</string>
<string name="not_selected" msgid="410652016565864475">"nincs kiválasztva"</string>
+ <string name="in_progress" msgid="2149208189184319441">"folyamatban"</string>
<string name="whichApplication" msgid="5432266899591255759">"Művelet végrehajtása a következővel:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Művelet elvégzése a(z) %1$s segítségével"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Művelet végrehajtása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a7992f968ea0..6fdf577d609a 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"նշված չէ"</string>
<string name="selected" msgid="6614607926197755875">"ընտրված է"</string>
<string name="not_selected" msgid="410652016565864475">"ընտրված չէ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ընթացքում է"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ավարտել գործողությունը` օգտագործելով"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Եզրափակել գործողությունը՝ օգտագործելով %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ավարտել գործողությունը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index c86b8cddb342..2a5d5b2d2042 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+ <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 667dd9c46055..3af415a6af88 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ekki valið"</string>
<string name="selected" msgid="6614607926197755875">"valið"</string>
<string name="not_selected" msgid="410652016565864475">"ekki valið"</string>
+ <string name="in_progress" msgid="2149208189184319441">"í gangi"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ljúka aðgerð með"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ljúka aðgerð með %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ljúka aðgerð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6919a3dc6c7c..b35132e22d41 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
<string name="selected" msgid="6614607926197755875">"selezionato"</string>
<string name="not_selected" msgid="410652016565864475">"non selezionato"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In corso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa azione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index cd8cb90ae608..3eb23dc21ca1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"לא מסומן"</string>
<string name="selected" msgid="6614607926197755875">"נבחר"</string>
<string name="not_selected" msgid="410652016565864475">"לא נבחר"</string>
+ <string name="in_progress" msgid="2149208189184319441">"בתהליך"</string>
<string name="whichApplication" msgid="5432266899591255759">"השלמת הפעולה באמצעות"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏השלמת הפעולה באמצעות %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"להשלמת הפעולה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index a9226379b446..199bc2e98b5f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"OFF"</string>
<string name="selected" msgid="6614607926197755875">"選択済み"</string>
<string name="not_selected" msgid="410652016565864475">"未選択"</string>
+ <string name="in_progress" msgid="2149208189184319441">"進行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"アプリケーションを選択"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sを使用してアクションを完了"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"アクションを実行"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 8765659347f9..09b3c2b6b952 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"არ არის მონიშნული"</string>
<string name="selected" msgid="6614607926197755875">"არჩეულია"</string>
<string name="not_selected" msgid="410652016565864475">"არ არის არჩეული"</string>
+ <string name="in_progress" msgid="2149208189184319441">"მიმდინარეობს"</string>
<string name="whichApplication" msgid="5432266899591255759">"რა გამოვიყენოთ?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"მოქმედების %1$s-ის გამოყენებით დასრულება"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"მოქმედების დასრულება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6e8281326606..d9fde8fba397 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгіленбеген"</string>
<string name="selected" msgid="6614607926197755875">"таңдалған"</string>
<string name="not_selected" msgid="410652016565864475">"таңдалмаған"</string>
+ <string name="in_progress" msgid="2149208189184319441">"орындалуда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Әрекетті аяқтау"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Әрекетті %1$s қолданбасын пайдаланып аяқтау"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Әрекетті аяқтау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index d56a55ded4a9..716e810c62e1 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"មិន​បាន​ធីក​"</string>
<string name="selected" msgid="6614607926197755875">"បាន​ជ្រើសរើស"</string>
<string name="not_selected" msgid="410652016565864475">"មិនបានជ្រើសរើសទេ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ​ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a7d6ae0d1fbe..716b3586ff04 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string>
<string name="selected" msgid="6614607926197755875">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
<string name="not_selected" msgid="410652016565864475">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index f5d16fcb3f7f..7ebb463a17a7 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"선택 안함"</string>
<string name="selected" msgid="6614607926197755875">"선택됨"</string>
<string name="not_selected" msgid="410652016565864475">"선택되지 않음"</string>
+ <string name="in_progress" msgid="2149208189184319441">"진행 중"</string>
<string name="whichApplication" msgid="5432266899591255759">"작업을 수행할 때 사용하는 애플리케이션"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s을(를) 사용하여 작업 완료"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"작업 완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 174d7324dda9..51ff4f9226fd 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
<string name="selected" msgid="6614607926197755875">"тандалган"</string>
<string name="not_selected" msgid="410652016565864475">"тандалган жок"</string>
+ <string name="in_progress" msgid="2149208189184319441">"аткарылууда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 34da0f4ec111..0fc6b825e39e 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ບໍ່ໄດ້ໝາຍຖືກ"</string>
<string name="selected" msgid="6614607926197755875">"ເລືອກແລ້ວ"</string>
<string name="not_selected" msgid="410652016565864475">"ບໍ່ໄດ້ເລືອກແລ້ວ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ກຳລັງດຳເນີນການ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ດຳເນີນການໂດຍໃຊ້"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ສຳ​ເລັດ​​​ການ​ດຳ​ເນີນ​ການ​ໂດຍ​ໃຊ້ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ສຳເລັດຄຳສັ່ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1879dcc85513..18ac1b1e37e0 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nepažymėta"</string>
<string name="selected" msgid="6614607926197755875">"pasirinkta"</string>
<string name="not_selected" msgid="410652016565864475">"nepasirinkta"</string>
+ <string name="in_progress" msgid="2149208189184319441">"vykdoma"</string>
<string name="whichApplication" msgid="5432266899591255759">"Užbaigti veiksmą naudojant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Užbaigti veiksmą naudojant %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Užbaigti veiksmą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 15eda966c48c..1799e388de33 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nav atzīmēts"</string>
<string name="selected" msgid="6614607926197755875">"atlasīts"</string>
<string name="not_selected" msgid="410652016565864475">"nav atlasīts"</string>
+ <string name="in_progress" msgid="2149208189184319441">"notiek apstrāde"</string>
<string name="whichApplication" msgid="5432266899591255759">"Izvēlieties lietotni"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Pabeigt darbību, izmantojot %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Pabeigt darbību"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b29d49ef8dbe..39c3b8ae90bc 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"во тек"</string>
<string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index c64160c793da..33587ae93a00 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"പരിശോധിക്കാത്തത്"</string>
<string name="selected" msgid="6614607926197755875">"തിരഞ്ഞെടുത്തു"</string>
<string name="not_selected" msgid="410652016565864475">"തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
+ <string name="in_progress" msgid="2149208189184319441">"പുരോഗതിയിലാണ്"</string>
<string name="whichApplication" msgid="5432266899591255759">"പൂർണ്ണമായ പ്രവർത്തനം ഉപയോഗിക്കുന്നു"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ഉപയോഗിച്ച് പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 424401584b83..e17b884979b2 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"тэмдэглээгүй"</string>
<string name="selected" msgid="6614607926197755875">"сонгосон"</string>
<string name="not_selected" msgid="410652016565864475">"сонгоогүй"</string>
+ <string name="in_progress" msgid="2149208189184319441">"үргэлжилж байна"</string>
<string name="whichApplication" msgid="5432266899591255759">"Үйлдлийг дуусгах"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ашиглан үйлдлийг гүйцээх"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Үйлдлийг дуусгах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 28522d7e564c..5195055d2f5c 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string>
<string name="selected" msgid="6614607926197755875">"निवडला"</string>
<string name="not_selected" msgid="410652016565864475">"निवडला नाही"</string>
+ <string name="in_progress" msgid="2149208189184319441">"प्रगतीपथावर आहे"</string>
<string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"क्रिया पूर्ण झाली"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c156c6a90596..14e3fccb5cdb 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak ditandai"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+ <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 14409d895099..74ecc1d5d2b5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
<string name="selected" msgid="6614607926197755875">"ရွေးချယ်ထားသည်"</string>
<string name="not_selected" msgid="410652016565864475">"ရွေးချယ်မထားပါ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ဆောင်ရွက်နေသည်"</string>
<string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d046447c82c8..4394e4958bf4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ikke avmerket"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Fullfør med"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Fullfør handlingen med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Fullfør handlingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 36af211f07b3..33b57d1cc9e2 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
<string name="selected" msgid="6614607926197755875">"चयन गरियो"</string>
<string name="not_selected" msgid="410652016565864475">"चयन गरिएन"</string>
+ <string name="in_progress" msgid="2149208189184319441">"जारी छ"</string>
<string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 75857ff45ccb..0c6105472182 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"niet aangevinkt"</string>
<string name="selected" msgid="6614607926197755875">"geselecteerd"</string>
<string name="not_selected" msgid="410652016565864475">"niet geselecteerd"</string>
+ <string name="in_progress" msgid="2149208189184319441">"bezig"</string>
<string name="whichApplication" msgid="5432266899591255759">"Actie voltooien met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Actie voltooien via %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Actie voltooien"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index a76852f84895..96b1ff7a6626 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ଯାଞ୍ଚ ହୋଇନାହିଁ"</string>
<string name="selected" msgid="6614607926197755875">"ଚୟନ କରାଯାଇଛି"</string>
<string name="not_selected" msgid="410652016565864475">"ଚୟନ କରାଯାଇନାହିଁ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ଚାଲୁଅଛି"</string>
<string name="whichApplication" msgid="5432266899591255759">"ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 209c6a1fb88c..757745743623 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="selected" msgid="6614607926197755875">"ਚੁਣਿਆ ਗਿਆ"</string>
<string name="not_selected" msgid="410652016565864475">"ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ਜਾਰੀ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c45ac41462ae..2b8076ad57fe 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie wybrano"</string>
<string name="selected" msgid="6614607926197755875">"wybrano"</string>
<string name="not_selected" msgid="410652016565864475">"nie wybrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"w toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Wykonaj czynność przez..."</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Wykonaj czynność w aplikacji %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Wykonaj działanie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 8198ae939e3b..ab5f17621ce4 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 57e1cb0e4e74..08c488eff97d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 8198ae939e3b..ab5f17621ce4 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ae0a62901a94..c20a590c5238 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nebifat"</string>
<string name="selected" msgid="6614607926197755875">"selectat"</string>
<string name="not_selected" msgid="410652016565864475">"neselectat"</string>
+ <string name="in_progress" msgid="2149208189184319441">"în curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizați acțiunea"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8d3edf1d79e3..9c961ba9c904 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"не отмечено"</string>
<string name="selected" msgid="6614607926197755875">"выбрано"</string>
<string name="not_selected" msgid="410652016565864475">"не выбрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"в процессе"</string>
<string name="whichApplication" msgid="5432266899591255759">"Что использовать?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Выполнить с помощью приложения \"%1$s\""</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Выполнить действие"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 132ee6f261c7..2b20e66be7c6 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"පරීක්ෂා කර නැත"</string>
<string name="selected" msgid="6614607926197755875">"තෝරන ලදි"</string>
<string name="not_selected" msgid="410652016565864475">"තෝරා නොමැත"</string>
+ <string name="in_progress" msgid="2149208189184319441">"සිදු වෙමින් පවතී"</string>
<string name="whichApplication" msgid="5432266899591255759">"පහත භාවිතයෙන් ක්‍රියාව සම්පූර්ණ කරන්න"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s භාවිතා කරමින් ක්‍රියාව සම්පුර්ණ කරන්න"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ක්‍රියාව සම්පූර්ණ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index afdf1d9a4b21..46be94f867ef 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nezačiarknuté"</string>
<string name="selected" msgid="6614607926197755875">"vybrané"</string>
<string name="not_selected" msgid="410652016565864475">"nevybrané"</string>
+ <string name="in_progress" msgid="2149208189184319441">"prebieha"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončiť akciu pomocou aplikácie"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončiť akciu pomocou aplikácie %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončiť akciu"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index bebdf7a71828..f029b7a35fac 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
<string name="selected" msgid="6614607926197755875">"izbrano"</string>
<string name="not_selected" msgid="410652016565864475">"ni izbrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"v teku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 25e2820611f6..bf04c0e2b4cd 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"nuk u përzgjodh"</string>
<string name="selected" msgid="6614607926197755875">"i zgjedhur"</string>
<string name="not_selected" msgid="410652016565864475">"i pazgjedhur"</string>
+ <string name="in_progress" msgid="2149208189184319441">"në vazhdim"</string>
<string name="whichApplication" msgid="5432266899591255759">"Përfundo veprimin duke përdorur"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Përfundo veprimin duke përdorur %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Përfundo veprimin"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index cd03394c58d4..5261bd3f43df 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
<string name="selected" msgid="6614607926197755875">"изабрано"</string>
<string name="not_selected" msgid="410652016565864475">"није изабрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"у току"</string>
<string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cb0b420d7f17..1b54981c1a34 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"inte markerad"</string>
<string name="selected" msgid="6614607926197755875">"valt"</string>
<string name="not_selected" msgid="410652016565864475">"inte valt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fbee2c3d8ff8..41664782148c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"haijateuliwa"</string>
<string name="selected" msgid="6614607926197755875">"imechaguliwa"</string>
<string name="not_selected" msgid="410652016565864475">"haijachaguliwa"</string>
+ <string name="in_progress" msgid="2149208189184319441">"inaendelea"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kamilisha kitendo ukitumia"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Kamilisha kitendo ukitumia %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Kamilisha kitendo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 18b588cd395c..63a5805991fc 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"முடக்கப்பட்டுள்ளது"</string>
<string name="selected" msgid="6614607926197755875">"தேர்ந்தெடுக்கப்பட்டது"</string>
<string name="not_selected" msgid="410652016565864475">"தேர்ந்தெடுக்கப்படவில்லை"</string>
+ <string name="in_progress" msgid="2149208189184319441">"செயலிலுள்ளது"</string>
<string name="whichApplication" msgid="5432266899591255759">"இதைப் பயன்படுத்தி செயலை நிறைவுசெய்"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ஐப் பயன்படுத்தி செயலை முடிக்கவும்"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"செயலை முடி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 3568871008cd..ff5acd6add82 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string>
<string name="selected" msgid="6614607926197755875">"ఎంచుకోబడింది"</string>
<string name="not_selected" msgid="410652016565864475">"ఎంచుకోబడలేదు"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ప్రోగ్రెస్‌లో ఉంది"</string>
<string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"చర్యను పూర్తి చేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index ed7ae5ba82df..82e5417e2261 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ยังไม่เลือก"</string>
<string name="selected" msgid="6614607926197755875">"เลือกไว้"</string>
<string name="not_selected" msgid="410652016565864475">"ไม่ได้เลือกไว้"</string>
+ <string name="in_progress" msgid="2149208189184319441">"กำลังดำเนินการ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ทำงานให้เสร็จโดยใช้"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ดำเนินการให้เสร็จสมบูรณ์โดยใช้ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ทำงานให้เสร็จสิ้น"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 73848a33abb8..a21c4f05a686 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"hindi nilagyan ng check"</string>
<string name="selected" msgid="6614607926197755875">"pinili"</string>
<string name="not_selected" msgid="410652016565864475">"hindi pinili"</string>
+ <string name="in_progress" msgid="2149208189184319441">"isinasagawa"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kumpletuhin ang pagkilos gamit ang"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Tapusin ang pagkilos gamit ang %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Gawin ang pagkilos"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bc1c66447c25..be9815d24741 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"işaretli değil"</string>
<string name="selected" msgid="6614607926197755875">"seçili"</string>
<string name="not_selected" msgid="410652016565864475">"seçili değil"</string>
+ <string name="in_progress" msgid="2149208189184319441">"devam ediyor"</string>
<string name="whichApplication" msgid="5432266899591255759">"İşlemi şunu kullanarak tamamla"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"İşlemi %1$s kullanarak tamamla"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"İşlemi tamamla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 1b4598add33a..03788fae8ed5 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
<string name="selected" msgid="6614607926197755875">"вибрано"</string>
<string name="not_selected" msgid="410652016565864475">"не вибрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"триває"</string>
<string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3f9cee11da36..78758e805039 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"چیک نہیں کیا گیا"</string>
<string name="selected" msgid="6614607926197755875">"منتخب کردہ"</string>
<string name="not_selected" msgid="410652016565864475">"غیر منتخب کردہ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"جاری ہے"</string>
<string name="whichApplication" msgid="5432266899591255759">"اس کا استعمال کرکے کارروائی مکمل کریں"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏%1$s کا استعمال کر کے کارروائی مکمل کریں"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"کارروائی مکمل کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index df7d4670dc29..ecc5f524f90d 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
<string name="selected" msgid="6614607926197755875">"tanlangan"</string>
<string name="not_selected" msgid="410652016565864475">"tanlanmagan"</string>
+ <string name="in_progress" msgid="2149208189184319441">"bajarilmoqda"</string>
<string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 259696dc587c..38a424bc22d9 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
<string name="selected" msgid="6614607926197755875">"đã chọn"</string>
<string name="not_selected" msgid="410652016565864475">"chưa được chọn"</string>
+ <string name="in_progress" msgid="2149208189184319441">"đang thực hiện"</string>
<string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 794f50eb8069..cf2ce256a5b3 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾选"</string>
<string name="selected" msgid="6614607926197755875">"已选择"</string>
<string name="not_selected" msgid="410652016565864475">"未选择"</string>
+ <string name="in_progress" msgid="2149208189184319441">"进行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"选择要使用的应用:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"使用%1$s完成操作"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fa2fee5f4083..ccfb02628ed4 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"揀咗"</string>
<string name="not_selected" msgid="410652016565864475">"未揀"</string>
+ <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"完成操作需使用"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 798e06c3f53c..d6c98c5b6487 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"已選取"</string>
<string name="not_selected" msgid="410652016565864475">"未選取"</string>
+ <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"選擇要使用的應用程式"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f93b84463874..9c9d68a68141 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"akuhloliwe"</string>
<string name="selected" msgid="6614607926197755875">"okukhethiwe"</string>
<string name="not_selected" msgid="410652016565864475">"akukhethiwe"</string>
+ <string name="in_progress" msgid="2149208189184319441">"kuyaqhubeka"</string>
<string name="whichApplication" msgid="5432266899591255759">"Qedela isenzo usebenzisa"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Qedela isenzo usebenzisa i-%1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Qedela isenzo"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d052d70442c4..441aa2d6ecc1 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -285,9 +285,6 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system default text classifier -->
<flag name="textClassifier" value="0x10000" />
- <!-- Additional flag from base permission type: this permission can be automatically
- granted to the document manager -->
- <flag name="documenter" value="0x40000" />
<!-- Additional flag from base permission type: this permission automatically
granted to device configurator -->
<flag name="configurator" value="0x80000" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6d40216adb43..b50ff80ff4e0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3607,8 +3607,8 @@
-->
<integer name="config_largeScreenSmallestScreenWidthDp">600</integer>
- <!-- True if the device is using leagacy split. -->
- <bool name="config_useLegacySplit">true</bool>
+ <!-- True if the device is using legacy split. -->
+ <bool name="config_useLegacySplit">false</bool>
<!-- True if the device supports running activities on secondary displays. -->
<bool name="config_supportsMultiDisplay">true</bool>
@@ -4739,9 +4739,15 @@
MediaSessionService. -->
<string name="config_customMediaSessionPolicyProvider"></string>
+ <!-- The min scale for the wallpaper when it's zoomed out -->
+ <item name="config_wallpaperMinScale" format="float" type="dimen">1</item>
+
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item>
+ <!-- If true, the wallpaper will scale regardless of the value of shouldZoomOutWallpaper() -->
+ <bool name="config_alwaysScaleWallpaper">false</bool>
+
<!-- Package name that will receive an explicit manifest broadcast for
android.os.action.POWER_SAVE_MODE_CHANGED. -->
<string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
@@ -4760,6 +4766,16 @@
<!-- pdp data reject retry delay in ms -->
<integer name="config_pdp_reject_retry_delay_ms">-1</integer>
+ <!-- Duration in milliseconds for device to vibrate on mash press on power
+ button. -->
+ <integer name="config_mashPressVibrateTimeOnPowerButton">0</integer>
+
+ <!-- Control the behavior when the user presses the power button 5 times.
+ 0 - Nothing
+ 1 - Launch panic button gesture
+ -->
+ <integer name="config_mashPressOnPowerBehavior">0</integer>
+
<!-- Whether or not to enable the binder heavy hitter watcher by default -->
<bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool>
@@ -4902,13 +4918,21 @@
<bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
<bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
- <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+ <bool name="config_cecHdmiCecVersion14b_default">false</bool>
<bool name="config_cecHdmiCecVersion20_allowed">true</bool>
- <bool name="config_cecHdmiCecVersion20_default">false</bool>
+ <bool name="config_cecHdmiCecVersion20_default">true</bool>
+
+ <bool name="config_cecRoutingControl_userConfigurable">true</bool>
+ <bool name="config_cecRoutingControlEnabled_allowed">true</bool>
+ <bool name="config_cecRoutingControlEnabled_default">false</bool>
+ <bool name="config_cecRoutingControlDisabled_allowed">true</bool>
+ <bool name="config_cecRoutingControlDisabled_default">true</bool>
<bool name="config_cecPowerControlMode_userConfigurable">true</bool>
<bool name="config_cecPowerControlModeTv_allowed">true</bool>
- <bool name="config_cecPowerControlModeTv_default">true</bool>
+ <bool name="config_cecPowerControlModeTv_default">false</bool>
+ <bool name="config_cecPowerControlModeTvAndAudioSystem_allowed">true</bool>
+ <bool name="config_cecPowerControlModeTvAndAudioSystem_default">true</bool>
<bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
<bool name="config_cecPowerControlModeBroadcast_default">false</bool>
<bool name="config_cecPowerControlModeNone_allowed">true</bool>
@@ -4920,6 +4944,12 @@
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+ <bool name="config_cecSystemAudioControl_userConfigurable">true</bool>
+ <bool name="config_cecSystemAudioControlEnabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioControlEnabled_default">true</bool>
+ <bool name="config_cecSystemAudioControlDisabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioControlDisabled_default">false</bool>
+
<bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
<bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
<bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
@@ -5044,4 +5074,7 @@
<!-- the number of the max cached processes in the system. -->
<integer name="config_customizedMaxCachedProcesses">32</integer>
+
+ <!-- Whether this device should support taking app snapshots on closure -->
+ <bool name="config_disableTaskSnapshots">false</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2403a605972e..a519cded69e4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3201,10 +3201,158 @@
<public type="string" name="config_defaultRingtoneVibrationSound" id="0x0104003b" />
<!-- ===============================================================
+ Resources added in version S-V2 of the platform
+
+ NOTE: add <public> elements within a <staging-public-group> like so:
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1" />
+ <public name="exampleAttr2" />
+ </staging-public-group>
+
+ To add a new <staging-public-group> block, find the id value for the
+ last <staging-public-group> block defined for thie API level, and
+ subtract 0x00010000 from it to get to the id of the new block.
+
+ For example, if the block closest to the end of this file has an id
+ of 0x01ee0000, the id of the new block should be 0x01ed0000
+ (0x01ee0000 - 0x00010000 = 0x01ed0000).
+ =============================================================== -->
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01fe0000">
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x01fd0000">
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01fc0000">
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01fb0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01fa0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01f90000">
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01f80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01f70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01f60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01f50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01f40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01f30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01f20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01f10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01f00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01ef0000">
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ee0000">
+ </staging-public-group>
+
+ <!-- ===============================================================
+ Resources added in version T of the platform
+
+ NOTE: add <public> elements within a <staging-public-group> like so:
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1" />
+ <public name="exampleAttr2" />
+ </staging-public-group>
+
+ To add a new <staging-public-group> block, find the id value for the
+ last <staging-public-group> block defined for thie API level, and
+ subtract 0x00010000 from it to get to the id of the new block.
+
+ For example, if the block closest to the end of this file has an id
+ of 0x01ee0000, the id of the new block should be 0x01ed0000
+ (0x01ee0000 - 0x00010000 = 0x01ed0000).
+ =============================================================== -->
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01df0000">
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01de0000">
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x0dfd0000">
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x0dfc0000">
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01db0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01da0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01d90000">
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01d80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01d70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01d60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01d50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01d40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01d30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01d20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01d10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01d00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01cf0000">
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ce0000">
+ </staging-public-group>
+
+ <!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
Any new items (attrs, styles, ids, etc.) *must* be added in a
- public-group block, as the preceding comment explains.
+ staging-public-group block, as the preceding comment explains.
Items added outside of a group may have their value recalculated
every time something new is added to this file.
=============================================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a99a22009e3b..d2c6f7880cb9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3195,6 +3195,9 @@
<!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
<string name="not_selected">not selected</string>
+ <!-- Default state description for indeterminate progressbar. [CHAR LIMIT=NONE] -->
+ <string name="in_progress">in progress</string>
+
<!-- Title of intent resolver dialog when selecting an application to run. -->
<string name="whichApplication">Complete action using</string>
<!-- Title of intent resolver dialog when selecting an application to run
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 40555fdaaa45..a940ef3fd7df 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -798,6 +798,7 @@
<java-symbol type="string" name="ime_action_previous" />
<java-symbol type="string" name="ime_action_search" />
<java-symbol type="string" name="ime_action_send" />
+ <java-symbol type="string" name="in_progress" />
<java-symbol type="string" name="invalidPin" />
<java-symbol type="string" name="js_dialog_before_unload_positive_button" />
<java-symbol type="string" name="js_dialog_before_unload_negative_button" />
@@ -4312,9 +4313,17 @@
<java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
<java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
+ <java-symbol type="bool" name="config_cecRoutingControl_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRoutingControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecRoutingControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecRoutingControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecRoutingControlDisabled_default" />
+
<java-symbol type="bool" name="config_cecPowerControlMode_userConfigurable" />
<java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
<java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_allowed" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_default" />
<java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
<java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
<java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
@@ -4326,6 +4335,12 @@
<java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
<java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioControl_userConfigurable" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_default" />
+
<java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
<java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
<java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
@@ -4425,4 +4440,6 @@
<java-symbol type="bool" name="config_volumeShowRemoteSessions" />
<java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
+
+ <java-symbol type="bool" name="config_disableTaskSnapshots" />
</resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 873b9ebe4f1a..5ad37484bcb8 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,18 +18,18 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="9">
- <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
- reminders="false" events="false" repeatCallers="true" convos="false"
- convosFrom="3"/>
+<zen version="10">
+ <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="true"
+ messagesFrom="2" reminders="false" events="false" repeatCallers="true" convos="true"
+ convosFrom="2"/>
<automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
component="android/com.android.server.notification.EventConditionProvider"
conditionId="condition://android/event?userId=-10000&amp;calendar=&amp;reply=1"/>
<automatic ruleId="EVERY_NIGHT_DEFAULT_RULE" enabled="false" snoozing="false" name="Sleeping"
zen="1" component="android/com.android.server.notification.ScheduleConditionProvider"
conditionId="condition://android/schedule?days=1.2.3.4.5.6.7&amp;start=22.0&amp;end=7.0&amp;exitAtAlarm=true"/>
- <!-- all visual effects that exist as of P -->
- <disallow visualEffects="511" />
+ <!-- everything when screen off (for old target sdks); fullscreen; lights; peek; ambient -->
+ <disallow visualEffects="157" />
<!-- whether there are notification channels that can bypass dnd -->
<state areChannelsBypassingDnd="false" />
</zen>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 2650d9f69dd7..2be5c4746345 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -245,6 +245,9 @@
<!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
<shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
+ <!-- Taiwan -->
+ <shortcode country="tw" pattern="\\d{4}" free="1922" />
+
<!-- Thailand: 4186001 used by AIS_TH_DCB -->
<shortcode country="th" pattern="\\d{1,5}" premium="4\\d{6}" free="4186001" />
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
index c65ef9a56cd8..53ba140e6aad 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
@@ -16,18 +16,40 @@
package android.accessibilityservice;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.window.WindowTokenClient;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,6 +64,8 @@ import org.mockito.MockitoAnnotations;
public class AccessibilityServiceTest {
private static final String TAG = "AccessibilityServiceTest";
private static final int CONNECTION_ID = 1;
+ private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
+ TYPE_ACCESSIBILITY_OVERLAY);
private static class AccessibilityServiceTestClass extends AccessibilityService {
private IAccessibilityServiceClient mCallback;
@@ -49,7 +73,11 @@ public class AccessibilityServiceTest {
AccessibilityServiceTestClass() {
super();
- attachBaseContext(InstrumentationRegistry.getContext());
+ Context context = ApplicationProvider.getApplicationContext();
+ final Display display = context.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+
+ attachBaseContext(context.createTokenContext(new WindowTokenClient(), display));
mLooper = InstrumentationRegistry.getContext().getMainLooper();
}
@@ -78,14 +106,33 @@ public class AccessibilityServiceTest {
private @Mock IBinder mMockIBinder;
private IAccessibilityServiceClient mServiceInterface;
private AccessibilityServiceTestClass mService;
+ private final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
@Before
- public void setUp() throws RemoteException {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mService = new AccessibilityServiceTestClass();
+ mService.onCreate();
mService.setupCallback(mMockClientForCallback);
mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder);
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ final int displayId = (int) args[0];
+ final IBinder token = new Binder();
+ WindowManagerGlobal.getWindowManagerService().addWindowToken(token,
+ TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */);
+ mWindowTokens.put(displayId, token);
+ return token;
+ }).when(mMockConnection).getOverlayWindowToken(anyInt());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ for (int i = mWindowTokens.size() - 1; i >= 0; --i) {
+ WindowManagerGlobal.getWindowManagerService().removeWindowToken(
+ mWindowTokens.valueAt(i), mWindowTokens.keyAt(i));
+ }
}
@Test
@@ -101,4 +148,79 @@ public class AccessibilityServiceTest {
verify(mMockConnection).getSystemActions();
}
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedDisplayContext() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createDisplayContext(session.getDisplay());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedWindowContext() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createDisplayContext(session.getDisplay())
+ .createWindowContext(TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedWindowContextWithDisplay() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createWindowContext(session.getDisplay(),
+ TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test(expected = WindowManager.BadTokenException.class)
+ public void testAddViewWithA11yServiceDerivedWindowContextWithDifferentType()
+ throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createWindowContext(session.getDisplay(),
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+
+ private static class VirtualDisplaySession implements AutoCloseable {
+ private final VirtualDisplay mVirtualDisplay;
+
+ VirtualDisplaySession() {
+ final DisplayManager displayManager = ApplicationProvider.getApplicationContext()
+ .getSystemService(DisplayManager.class);
+ final int width = 800;
+ final int height = 480;
+ final int density = 160;
+ ImageReader reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 2 /* maxImages */);
+ mVirtualDisplay = displayManager.createVirtualDisplay(
+ TAG, width, height, density, reader.getSurface(),
+ VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ }
+
+ private Display getDisplay() {
+ return mVirtualDisplay.getDisplay();
+ }
+
+ @Override
+ public void close() throws Exception {
+ mVirtualDisplay.release();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 4b0ed65e5fde..2c3c1ed0c40e 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -26,8 +26,11 @@ import android.content.pm.PackageInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import androidx.annotation.NonNull;
import androidx.test.filters.LargeTest;
+import com.android.internal.annotations.VisibleForTesting;
+
import junit.framework.TestCase;
import org.mockito.Mockito;
@@ -110,6 +113,13 @@ public class ApplicationPackageManagerTest extends TestCase {
public boolean isAllow3rdPartyOnInternal(Context context) {
return mAllow3rdPartyOnInternal;
}
+
+ @Override
+ @VisibleForTesting
+ protected @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app,
+ StorageManager storageManager, IPackageManager pm) {
+ return super.getPackageCandidateVolumes(app, storageManager, pm);
+ }
}
private StorageManager getMockedStorageManager() {
@@ -223,7 +233,7 @@ public class ApplicationPackageManagerTest extends TestCase {
appInfo.flags = 0;
appInfo.volumeUuid = sInternalVolUuid;
- Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
+ Mockito.when(pm.isPackageDeviceAdminOnAnyUser(appInfo.packageName)).thenReturn(false);
appPkgMgr.setAllow3rdPartyOnInternal(true);
List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
appInfo, storageManager, pm);
@@ -231,7 +241,7 @@ public class ApplicationPackageManagerTest extends TestCase {
appInfo.volumeUuid = sInternalVolUuid;
appPkgMgr.setAllow3rdPartyOnInternal(true);
- Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true);
+ Mockito.when(pm.isPackageDeviceAdminOnAnyUser(appInfo.packageName)).thenReturn(true);
candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
verifyReturnedVolumes(candidates, sInternalVol);
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 8c05978ad03c..7a2c63f70dd1 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import androidx.test.filters.SmallTest;
+import org.junit.After;
import org.junit.Test;
/**
@@ -57,8 +58,70 @@ public class PropertyInvalidatedCacheTests {
}
}
+ // Clear the test mode after every test, in case this process is used for other tests.
+ @After
+ public void tearDown() throws Exception {
+ PropertyInvalidatedCache.setTestMode(false);
+ }
+
+ // This test is disabled pending an sepolicy change that allows any app to set the
+ // test property.
+ @Test
+ public void testBasicCache() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Create a cache that uses simple arithmetic to computer its values.
+ PropertyInvalidatedCache<Integer, Boolean> testCache =
+ new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer x) {
+ return tester.query(x);
+ }
+ @Override
+ protected boolean bypass(Integer x) {
+ return x % 13 == 0;
+ }
+ };
+
+ PropertyInvalidatedCache.setTestMode(true);
+ PropertyInvalidatedCache.testPropertyName(CACHE_PROPERTY);
+
+ tester.verify(0);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(1);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(2);
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(3);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(4);
+
+ // Invalidate the cache, and verify that the next read on 3 goes to the server.
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(5);
+
+ // Test bypass. The query for 13 always bypasses the cache.
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(8);
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(9);
+ }
+
@Test
- public void testDisableCache1() {
+ public void testDisableCache() {
// A stand-in for the binder. The test verifies that calls are passed through to
// this class properly.
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index d1776fb7e5c1..3d7d807ca53d 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -32,7 +32,6 @@ import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
-import android.inputmethodservice.InputMethodService;
import android.media.ImageReader;
import android.os.UserHandle;
import android.view.Display;
@@ -140,13 +139,6 @@ public class ContextTest {
}
@Test
- public void testIsUiContext_InputMethodService_returnsTrue() {
- final InputMethodService ims = new InputMethodService();
-
- assertTrue(ims.isUiContext());
- }
-
- @Test
public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);
}
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 49b720cfba07..cf7e5c663ba7 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -15,19 +15,18 @@
*/
package android.content.pm;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.AUTH;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.PERMISSION;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.ROLLBACK;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
+import static android.content.pm.SigningDetails.CertCapabilities.AUTH;
+import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
+import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
+import static android.content.pm.SigningDetails.CertCapabilities.ROLLBACK;
+import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.content.pm.PackageParser.SigningDetails;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -990,11 +989,11 @@ public class SigningDetailsTest {
private void assertSigningDetailsContainsLineage(SigningDetails details,
String... pastSigners) {
// This method should only be invoked for results that contain a single signer.
- assertEquals(1, details.signatures.length);
- assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase(
+ assertEquals(1, details.getSignatures().length);
+ assertTrue(details.getSignatures()[0].toCharsString().equalsIgnoreCase(
pastSigners[pastSigners.length - 1]));
Set<String> signatures = new ArraySet<>(pastSigners);
- for (Signature pastSignature : details.pastSigningCertificates) {
+ for (Signature pastSignature : details.getPastSigningCertificates()) {
assertTrue(signatures.remove(pastSignature.toCharsString()));
}
assertEquals(0, signatures.size());
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index 22f6ec0b24b1..701e6194d4ee 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.fail;
+import android.graphics.fonts.FontCustomizationParser;
import android.graphics.fonts.FontStyle;
import android.os.LocaleList;
import android.text.FontConfig;
@@ -46,6 +47,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -318,6 +320,52 @@ public final class FontListParserTest {
}
}
+ @Test
+ public void alias() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font>test.ttf</font>"
+ + " </family>"
+ + " <family name='custom-family'>"
+ + " <font>missing.ttf</font>"
+ + " </family>"
+ + " <alias name='custom-alias' to='sans-serif'/>"
+ + "</familyset>";
+ FontConfig config = readFamilies(xml, true /* include non-existing font files */);
+ List<FontConfig.Alias> aliases = config.getAliases();
+ assertThat(aliases.size()).isEqualTo(1);
+ assertThat(aliases.get(0).getName()).isEqualTo("custom-alias");
+ assertThat(aliases.get(0).getOriginal()).isEqualTo("sans-serif");
+ }
+
+ @Test
+ public void dropped_FamilyAlias() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font>test.ttf</font>"
+ + " </family>"
+ + " <family name='custom-family'>"
+ + " <font>missing.ttf</font>"
+ + " </family>"
+ + " <alias name='custom-alias' to='custom-family'/>"
+ + "</familyset>";
+ FontConfig config = readFamilies(xml, false /* exclude not existing file */);
+ assertThat(config.getAliases()).isEmpty();
+ }
+
+ private FontConfig readFamilies(String xml, boolean allowNonExisting)
+ throws IOException, XmlPullParserException {
+ ByteArrayInputStream buffer = new ByteArrayInputStream(
+ xml.getBytes(StandardCharsets.UTF_8));
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(buffer, "UTF-8");
+ parser.nextTag();
+ return FontListParser.readFamilies(parser, "", new FontCustomizationParser.Result(), null,
+ 0L /* last modified date */, 0 /* config version */, allowNonExisting);
+ }
+
private FontConfig.FontFamily readFamily(String xml)
throws IOException, XmlPullParserException {
ByteArrayInputStream buffer = new ByteArrayInputStream(
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd39cdee4c32..fd7753b66a46 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -773,46 +773,51 @@ public class DeviceConfigTest {
try {
// Ensure the device starts in a known state.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
// Assert starting state.
- assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
assertThat(DeviceConfig.setProperties(properties1)).isTrue();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE);
// Test disabled (persistent). Persistence is not actually tested, that would require
// a host test.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
- assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
assertThat(DeviceConfig.setProperties(properties2)).isFalse();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE);
// Return to not disabled.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
- assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
assertThat(DeviceConfig.setProperties(properties2)).isTrue();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE2);
// Test disabled (persistent). Absence of persistence is not actually tested, that would
// require a host test.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
- assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
assertThat(DeviceConfig.setProperties(properties1)).isFalse();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE2);
// Return to not disabled.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
- assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
assertThat(DeviceConfig.setProperties(properties1)).isTrue();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE);
} finally {
// Try to return to the default sync disabled state in case of failure.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
// NAMESPACE will be cleared by cleanUp()
}
@@ -827,4 +832,80 @@ public class DeviceConfigTest {
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
}
+ @Test
+ public void deleteProperty_nullNamespace() {
+ try {
+ DeviceConfig.deleteProperty(null, KEY);
+ Assert.fail("Null namespace should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deleteProperty_nullName() {
+ try {
+ DeviceConfig.deleteProperty(NAMESPACE, null);
+ Assert.fail("Null name should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deletePropertyString() {
+ final String value = "new_value";
+ final String default_value = "default";
+ DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyBoolean() {
+ final boolean value = true;
+ final boolean default_value = false;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyInt() {
+ final int value = 123;
+ final int default_value = 999;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyLong() {
+ final long value = 456789;
+ final long default_value = 123456;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyFloat() {
+ final float value = 456.789f;
+ final float default_value = 123.456f;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deleteProperty_empty() {
+ assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
+ assertThat(result).isNull();
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 46c96c94dc11..2c8c38532d10 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -21,6 +21,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.os.Parcel;
+import android.view.Display;
import androidx.test.runner.AndroidJUnit4;
@@ -41,14 +42,14 @@ public class AccessibilityEventTest {
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 34;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
// and assertEqualAccessibilityRecord
/** The number of properties of the {@link AccessibilityRecord} class. */
- private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 24;
+ private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 25;
@Test
public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
@@ -69,6 +70,14 @@ public class AccessibilityEventTest {
}
@Test
+ public void testSourceDisplayId_getSetWorkAcrossParceling() {
+ final int sourceDisplayId = Display.DEFAULT_DISPLAY;
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setDisplayId(sourceDisplayId);
+ assertEquals(sourceDisplayId, copyEventViaParcel(event).getDisplayId());
+ }
+
+ @Test
public void testWindowChanges_getSetWorkAcrossParceling() {
final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
| AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 8643a37bba8d..3045d7d575d9 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -34,6 +34,8 @@ import java.util.List;
public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceConnection.Stub {
public void setServiceInfo(AccessibilityServiceInfo info) {}
+ public void setAttributionTag(String attributionTag) {}
+
public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
@@ -166,4 +168,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
public void logTrace(long timestamp, String where, String callingParams, int processId,
long threadId, int callingUid, Bundle callingStack) {}
+
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
}
diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java
new file mode 100644
index 000000000000..5b92471101c4
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ProgressBarTest {
+ private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private ProgressBar mBar;
+ private AccessibilityNodeInfo mInfo;
+
+ @Before
+ public void setUp() throws Exception {
+ // enable accessibility
+ mInstrumentation.getUiAutomation();
+ // create ProgressBar on main thread and call setProgress on main thread
+ mInstrumentation.runOnMainSync(() ->
+ mBar = new ProgressBar(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ null,
+ com.android.internal.R.attr.progressBarStyleHorizontal
+ )
+ );
+ mInfo = AccessibilityNodeInfo.obtain();
+ }
+
+ @After
+ public void tearDown() {
+ mInfo.recycle();
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_default() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("50%", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_custom_viewApi() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+ // A workaround for the not-attached ProgressBar.
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ info.setStateDescription(host.getStateDescription());
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
+ mBar.setStateDescription("custom state");
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ assertEquals("custom state", mBar.getStateDescription().toString());
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+
+ mBar.setStateDescription(null);
+
+ assertNull(mBar.getStateDescription());
+ mInfo.recycle();
+ mInfo = AccessibilityNodeInfo.obtain();
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("50%", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_custom_accessibilityNodeInfoApi() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setStateDescription("custom state");
+ }
+ });
+
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_default() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("in progress", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_custom_viewApi() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+ // A workaround for the not-attached ProgressBar.
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ info.setStateDescription(host.getStateDescription());
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
+ mBar.setStateDescription("custom state");
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ assertEquals("custom state", mBar.getStateDescription().toString());
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+
+ mBar.setStateDescription(null);
+
+ assertNull(mBar.getStateDescription());
+ mInfo.recycle();
+ mInfo = AccessibilityNodeInfo.obtain();
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("in progress", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_custom_accessibilityNodeInfoApi() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setStateDescription("custom state");
+ }
+ });
+
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 020f4a06b892..a6e351d9cee7 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -22,12 +22,15 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.IWindowManager;
@@ -38,6 +41,8 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Tests for {@link WindowContextController}
@@ -53,15 +58,18 @@ import org.junit.runner.RunWith;
@Presubmit
public class WindowContextControllerTest {
private WindowContextController mController;
+ @Mock
private IWindowManager mMockWms;
+ @Mock
+ private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
- mMockWms = mock(IWindowManager.class);
- mController = new WindowContextController(new Binder(), mMockWms);
-
- doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(),
- anyInt(), any());
+ MockitoAnnotations.initMocks(this);
+ mController = new WindowContextController(mMockToken, mMockWms);
+ doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
+ doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
+ anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -85,6 +93,8 @@ public class WindowContextControllerTest {
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
+ verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
+ eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
new file mode 100644
index 000000000000..045b3a27456f
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.accessibility;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.text.ParcelableSpan;
+import android.text.SpannableString;
+import android.text.style.LocaleSpan;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.util.AccessibilityUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Unit tests for AccessibilityUtils.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityUtilsTest {
+ @Test
+ public void textOrSpanChanged_stringChange_returnTextChange() {
+ final CharSequence beforeText = "a";
+
+ final CharSequence afterText = "b";
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.TEXT, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_stringNotChange_returnNoneChange() {
+ final CharSequence beforeText = "a";
+
+ final CharSequence afterText = "a";
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonSpanToNonParcelableSpan_returnNoneChange() {
+ final Object nonParcelableSpan = new Object();
+ final CharSequence beforeText = new SpannableString("a");
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonSpanToParcelableSpan_returnParcelableSpanChange() {
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final CharSequence beforeText = new SpannableString("a");
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonParcelableSpanToParcelableSpan_returnParcelableSpanChange() {
+ final Object nonParcelableSpan = new Object();
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonParcelableSpanChange_returnNoneChange() {
+ final Object nonParcelableSpan = new Object();
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(nonParcelableSpan, 1, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_parcelableSpanChange_returnParcelableSpanChange() {
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(parcelableSpan, 0, 1, 0);
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 1, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 272f2287dd6e..7d4412c7087d 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -24,6 +24,7 @@ import android.os.Binder;
import android.os.Parcel;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.view.InsetsState;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -59,7 +60,8 @@ public class RegisterStatusBarResultTest {
new Binder() /* imeToken */,
true /* navbarColorManagedByIme */,
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
- true /* appFullscreen */,
+ new InsetsState() /* requestedState */,
+ "test" /* packageName */,
new int[0] /* transientBarTypes */);
final RegisterStatusBarResult copy = clone(original);
@@ -79,7 +81,8 @@ public class RegisterStatusBarResultTest {
assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
assertThat(copy.mBehavior).isEqualTo(original.mBehavior);
- assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
+ assertThat(copy.mRequestedState).isEqualTo(original.mRequestedState);
+ assertThat(copy.mPackageName).isEqualTo(original.mPackageName);
assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes);
}
diff --git a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
new file mode 100644
index 000000000000..fdba811f3eaa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.util;
+
+import static com.android.internal.util.BitUtils.bytesToBEInt;
+import static com.android.internal.util.BitUtils.bytesToLEInt;
+import static com.android.internal.util.BitUtils.getUint16;
+import static com.android.internal.util.BitUtils.getUint32;
+import static com.android.internal.util.BitUtils.getUint8;
+import static com.android.internal.util.BitUtils.packBits;
+import static com.android.internal.util.BitUtils.uint16;
+import static com.android.internal.util.BitUtils.uint32;
+import static com.android.internal.util.BitUtils.uint8;
+import static com.android.internal.util.BitUtils.unpackBits;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BitUtilsTest {
+
+ @Test
+ public void testUnsignedByteWideningConversions() {
+ byte b0 = 0;
+ byte b1 = 1;
+ byte bm1 = -1;
+ assertEquals(0, uint8(b0));
+ assertEquals(1, uint8(b1));
+ assertEquals(127, uint8(Byte.MAX_VALUE));
+ assertEquals(128, uint8(Byte.MIN_VALUE));
+ assertEquals(255, uint8(bm1));
+ assertEquals(255, uint8((byte)255));
+ }
+
+ @Test
+ public void testUnsignedShortWideningConversions() {
+ short s0 = 0;
+ short s1 = 1;
+ short sm1 = -1;
+ assertEquals(0, uint16(s0));
+ assertEquals(1, uint16(s1));
+ assertEquals(32767, uint16(Short.MAX_VALUE));
+ assertEquals(32768, uint16(Short.MIN_VALUE));
+ assertEquals(65535, uint16(sm1));
+ assertEquals(65535, uint16((short)65535));
+ }
+
+ @Test
+ public void testUnsignedShortComposition() {
+ byte b0 = 0;
+ byte b1 = 1;
+ byte b2 = 2;
+ byte b10 = 10;
+ byte b16 = 16;
+ byte b128 = -128;
+ byte b224 = -32;
+ byte b255 = -1;
+ assertEquals(0x0000, uint16(b0, b0));
+ assertEquals(0xffff, uint16(b255, b255));
+ assertEquals(0x0a01, uint16(b10, b1));
+ assertEquals(0x8002, uint16(b128, b2));
+ assertEquals(0x01ff, uint16(b1, b255));
+ assertEquals(0x80ff, uint16(b128, b255));
+ assertEquals(0xe010, uint16(b224, b16));
+ }
+
+ @Test
+ public void testUnsignedIntWideningConversions() {
+ assertEquals(0, uint32(0));
+ assertEquals(1, uint32(1));
+ assertEquals(2147483647L, uint32(Integer.MAX_VALUE));
+ assertEquals(2147483648L, uint32(Integer.MIN_VALUE));
+ assertEquals(4294967295L, uint32(-1));
+ assertEquals(4294967295L, uint32((int)4294967295L));
+ }
+
+ @Test
+ public void testBytesToInt() {
+ assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0)));
+ assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255)));
+ assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1)));
+ assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2)));
+ assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255)));
+ assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1)));
+
+ assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0)));
+ assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1)));
+ assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255)));
+ }
+
+ @Test
+ public void testUnsignedGetters() {
+ ByteBuffer b = ByteBuffer.allocate(4);
+ b.putInt(0xffff);
+
+ assertEquals(0x0, getUint8(b, 0));
+ assertEquals(0x0, getUint8(b, 1));
+ assertEquals(0xff, getUint8(b, 2));
+ assertEquals(0xff, getUint8(b, 3));
+
+ assertEquals(0x0, getUint16(b, 0));
+ assertEquals(0xffff, getUint16(b, 2));
+
+ b.rewind();
+ b.putInt(0xffffffff);
+ assertEquals(0xffffffffL, getUint32(b, 0));
+ }
+
+ @Test
+ public void testBitsPacking() {
+ BitPackingTestCase[] testCases = {
+ new BitPackingTestCase(0, ints()),
+ new BitPackingTestCase(1, ints(0)),
+ new BitPackingTestCase(2, ints(1)),
+ new BitPackingTestCase(3, ints(0, 1)),
+ new BitPackingTestCase(4, ints(2)),
+ new BitPackingTestCase(6, ints(1, 2)),
+ new BitPackingTestCase(9, ints(0, 3)),
+ new BitPackingTestCase(~Long.MAX_VALUE, ints(63)),
+ new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)),
+ new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)),
+ };
+ for (BitPackingTestCase tc : testCases) {
+ int[] got = unpackBits(tc.packedBits);
+ assertTrue(
+ "unpackBits("
+ + tc.packedBits
+ + "): expected "
+ + Arrays.toString(tc.bits)
+ + " but got "
+ + Arrays.toString(got),
+ Arrays.equals(tc.bits, got));
+ }
+ for (BitPackingTestCase tc : testCases) {
+ long got = packBits(tc.bits);
+ assertEquals(
+ "packBits("
+ + Arrays.toString(tc.bits)
+ + "): expected "
+ + tc.packedBits
+ + " but got "
+ + got,
+ tc.packedBits,
+ got);
+ }
+
+ long[] moreTestCases = {
+ 0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(),
+ };
+ for (long l : moreTestCases) {
+ assertEquals(l, packBits(unpackBits(l)));
+ }
+ }
+
+ static byte[] bytes(int b1, int b2, int b3, int b4) {
+ return new byte[] {b(b1), b(b2), b(b3), b(b4)};
+ }
+
+ static byte b(int i) {
+ return (byte) i;
+ }
+
+ static int[] ints(int... array) {
+ return array;
+ }
+
+ static class BitPackingTestCase {
+ final int[] bits;
+ final long packedBits;
+
+ BitPackingTestCase(long packedBits, int[] bits) {
+ this.bits = bits;
+ this.packedBits = packedBits;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
new file mode 100644
index 000000000000..4497770ef40d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RingBufferTest {
+
+ @Test
+ public void testEmptyRingBuffer() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+ assertArrayEquals(new String[0], buffer.toArray());
+ }
+
+ @Test
+ public void testIncorrectConstructorArguments() {
+ try {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, -10);
+ fail("Should not be able to create a negative capacity RingBuffer");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 0);
+ fail("Should not be able to create a 0 capacity RingBuffer");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testRingBufferWithNoWrapping() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+ buffer.append("a");
+ buffer.append("b");
+ buffer.append("c");
+ buffer.append("d");
+ buffer.append("e");
+
+ String[] expected = {"a", "b", "c", "d", "e"};
+ assertArrayEquals(expected, buffer.toArray());
+ }
+
+ @Test
+ public void testRingBufferWithCapacity1() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 1);
+
+ buffer.append("a");
+ assertArrayEquals(new String[]{"a"}, buffer.toArray());
+
+ buffer.append("b");
+ assertArrayEquals(new String[]{"b"}, buffer.toArray());
+
+ buffer.append("c");
+ assertArrayEquals(new String[]{"c"}, buffer.toArray());
+
+ buffer.append("d");
+ assertArrayEquals(new String[]{"d"}, buffer.toArray());
+
+ buffer.append("e");
+ assertArrayEquals(new String[]{"e"}, buffer.toArray());
+ }
+
+ @Test
+ public void testRingBufferWithWrapping() {
+ int capacity = 100;
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, capacity);
+
+ buffer.append("a");
+ buffer.append("b");
+ buffer.append("c");
+ buffer.append("d");
+ buffer.append("e");
+
+ String[] expected1 = {"a", "b", "c", "d", "e"};
+ assertArrayEquals(expected1, buffer.toArray());
+
+ String[] expected2 = new String[capacity];
+ int firstIndex = 0;
+ int lastIndex = capacity - 1;
+
+ expected2[firstIndex] = "e";
+ for (int i = 1; i < capacity; i++) {
+ buffer.append("x");
+ expected2[i] = "x";
+ }
+ assertArrayEquals(expected2, buffer.toArray());
+
+ buffer.append("x");
+ expected2[firstIndex] = "x";
+ assertArrayEquals(expected2, buffer.toArray());
+
+ for (int i = 0; i < 10; i++) {
+ for (String s : expected2) {
+ buffer.append(s);
+ }
+ }
+ assertArrayEquals(expected2, buffer.toArray());
+
+ buffer.append("a");
+ expected2[lastIndex] = "a";
+ assertArrayEquals(expected2, buffer.toArray());
+ }
+
+ @Test
+ public void testGetNextSlot() {
+ int capacity = 100;
+ RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
+
+ final DummyClass1[] actual = new DummyClass1[capacity];
+ final DummyClass1[] expected = new DummyClass1[capacity];
+ for (int i = 0; i < capacity; ++i) {
+ final DummyClass1 obj = buffer.getNextSlot();
+ obj.x = capacity * i;
+ actual[i] = obj;
+ expected[i] = new DummyClass1();
+ expected[i].x = capacity * i;
+ }
+ assertArrayEquals(expected, buffer.toArray());
+
+ for (int i = 0; i < capacity; ++i) {
+ if (actual[i] != buffer.getNextSlot()) {
+ fail("getNextSlot() should re-use objects if available");
+ }
+ }
+
+ RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
+ assertNull("getNextSlot() should return null if the object can't be initiated "
+ + "(No nullary constructor)", buffer2.getNextSlot());
+
+ RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
+ assertNull("getNextSlot() should return null if the object can't be initiated "
+ + "(Inaccessible class)", buffer3.getNextSlot());
+ }
+
+ public static final class DummyClass1 {
+ int x;
+
+ public boolean equals(Object o) {
+ if (o instanceof DummyClass1) {
+ final DummyClass1 other = (DummyClass1) o;
+ return other.x == this.x;
+ }
+ return false;
+ }
+ }
+
+ public static final class DummyClass2 {
+ public DummyClass2(int x) {}
+ }
+
+ private static final class DummyClass3 {}
+}
diff --git a/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java
new file mode 100644
index 000000000000..996d7b435e5a
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.window;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+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.Mockito.mock;
+
+import android.app.ResourcesManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link ConfigurationHelper}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksMockingCoreTests:ConfigurationHelperTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ConfigurationHelperTest {
+ MockitoSession mMockitoSession;
+ ResourcesManager mResourcesManager;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(ResourcesManager.class)
+ .startMocking();
+ doReturn(mock(ResourcesManager.class)).when(ResourcesManager::getInstance);
+ mResourcesManager = ResourcesManager.getInstance();
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testShouldUpdateResources_NullConfig_ReturnsTrue() {
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), null /* config */,
+ new Configuration(), new Configuration(), false /* displayChanged */,
+ null /* configChanged */)).isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DisplayChanged_ReturnsTrue() {
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
+ new Configuration(), new Configuration(), true /* displayChanged */,
+ null /* configChanged */)).isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentResources_ReturnsTrue() {
+ doReturn(false).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
+ new Configuration(), new Configuration(), false /* displayChanged */,
+ null /* configChanged */)).isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentBounds_ReturnsTrue() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ config.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+ config.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
+
+ final Configuration newConfig = new Configuration();
+ newConfig.windowConfiguration.setBounds(new Rect(0, 0, 20, 20));
+ newConfig.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_SameConfig_ReturnsFalse() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isFalse();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentConfig_ReturnsTrue() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+ newConfig.setToDefaults();
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentNonPublicConfig_ReturnsTrue() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+ newConfig.windowConfiguration.setAppBounds(new Rect(0, 0, 10, 10));
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_OverrideConfigChanged_ReturnsFalse() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+ final boolean configChanged = true;
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, configChanged))
+ .isEqualTo(configChanged);
+ }
+}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 084e1dbced4a..d1e4322ab612 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -130,6 +130,13 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "allowed_privapp_com.google.android.car.adaslocation",
+ sub_dir: "permissions",
+ src: "com.google.android.car.adaslocation.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "allowed_privapp_com.google.android.car.kitchensink",
sub_dir: "permissions",
src: "com.google.android.car.kitchensink.xml",
diff --git a/data/etc/car/com.android.car.activityresolver.xml b/data/etc/car/com.android.car.activityresolver.xml
index d48bc15b1678..927c7387b175 100644
--- a/data/etc/car/com.android.car.activityresolver.xml
+++ b/data/etc/car/com.android.car.activityresolver.xml
@@ -19,3 +19,4 @@
<permission name="android.permission.MANAGE_USERS"/>
</privapp-permissions>
</permissions>
+
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/data/etc/car/com.google.android.car.adaslocation.xml
index bc9c203b299a..cc1ef3c3e160 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/data/etc/car/com.google.android.car.adaslocation.xml
@@ -14,18 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <TextView
- android:id="@+id/global_actions_change_message"
- android:layout_width="wrap_content"
- android:visibility="gone"
- android:layout_height="wrap_content"
- android:text="@string/global_actions_change_description" />
- <ImageView
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-</LinearLayout> \ No newline at end of file
+<permissions>
+ <privapp-permissions package="com.google.android.car.adaslocation">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 813b7995fe89..e1035f3963a9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -187,7 +187,6 @@ applications that come with the platform
<permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
<permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
<permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
- <permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SHUTDOWN"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="android.permission.STATUS_BAR"/>
@@ -342,6 +341,8 @@ applications that come with the platform
<!-- Needed for test only -->
<permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
<permission name="android.permission.POWER_SAVER" />
+ <!-- Needed for CTS tests -->
+ <permission name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"/>
<permission name="android.permission.READ_CARRIER_APP_INFO"/>
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
@@ -533,6 +534,11 @@ applications that come with the platform
<permission name="android.permission.CONTROL_VPN"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.wallpaper.livepicker">
+ <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
+ <permission name="android.permission.BIND_WALLPAPER"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.dynsystem">
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b67988ee9646..3e3723762800 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -79,12 +79,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-2029985709": {
- "message": "setFocusedTask: taskId=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_FOCUS",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"-2024464438": {
"message": "app-onAnimationFinished(): mOuter=%s",
"level": "DEBUG",
@@ -103,6 +97,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-2010331310": {
+ "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-2006946193": {
"message": "setClientVisible: %s clientVisible=%b Callers=%s",
"level": "VERBOSE",
@@ -181,6 +181,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1924376693": {
+ "message": " Setting Ready-group to %b. group=%s from %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-1918702467": {
"message": "onSyncFinishedDrawing %s",
"level": "VERBOSE",
@@ -211,6 +217,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "-1886145147": {
+ "message": "resumeTopActivity: Going to sleep and all paused",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1884933373": {
"message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
"level": "INFO",
@@ -247,12 +259,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
- "-1861864501": {
- "message": "resumeTopActivityLocked: Going to sleep and all paused",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1844540996": {
"message": " Initial targets: %s",
"level": "VERBOSE",
@@ -325,12 +331,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "-1768090656": {
- "message": "Re-launching after pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1750384749": {
"message": "Launch on display check: allow launch on public display",
"level": "DEBUG",
@@ -415,12 +415,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1655805455": {
- "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1647332198": {
"message": "remove RecentTask %s when finishing user %d",
"level": "INFO",
@@ -433,6 +427,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
},
+ "-1633115609": {
+ "message": "Key dispatch not paused for screen off",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1632122349": {
"message": "Changing surface while display frozen: %s",
"level": "VERBOSE",
@@ -487,6 +487,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-1564228464": {
+ "message": "App died while pausing: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1559645910": {
"message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s",
"level": "DEBUG",
@@ -565,12 +571,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
- "-1492696222": {
- "message": "App died during pause, not stopping: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1480772131": {
"message": "No app or window is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
@@ -613,6 +613,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1442613680": {
+ "message": " Creating Ready-group for Transition %d with root=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-1438175584": {
"message": "Input focus has changed to %s display=%d",
"level": "VERBOSE",
@@ -637,12 +643,24 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1421296808": {
+ "message": "Moving to RESUMED: %s (in existing)",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1419762046": {
"message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d",
"level": "DEBUG",
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "-1419461256": {
+ "message": "resumeTopActivity: Resumed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1413901262": {
"message": "startRecentsActivity(): intent=%s",
"level": "DEBUG",
@@ -709,6 +727,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-1311436264": {
+ "message": "Unregister task fragment organizer=%s uid=%d pid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-1305966693": {
"message": "Sending position change to %s, onTop: %b",
"level": "VERBOSE",
@@ -805,6 +829,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1187377055": {
+ "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1176488860": {
"message": "SURFACE isSecure=%b: %s",
"level": "INFO",
@@ -919,12 +949,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
- "-1066383762": {
- "message": "Sleep still waiting to pause %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1060365734": {
"message": "Attempted to add QS dialog window with bad token %s. Aborting.",
"level": "WARN",
@@ -985,6 +1009,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-957060823": {
+ "message": "Moving to PAUSING: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-951939129": {
"message": "Unregister task organizer=%s uid=%d",
"level": "VERBOSE",
@@ -1201,6 +1231,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-706481945": {
+ "message": "TaskFragment parent info changed name=%s parentTaskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-705939410": {
"message": "Waiting for pause to complete...",
"level": "VERBOSE",
@@ -1237,12 +1273,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-672228342": {
- "message": "resumeTopActivityLocked: Top activity resumed %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-668956537": {
"message": " THUMBNAIL %s: CREATE",
"level": "INFO",
@@ -1267,11 +1297,11 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "-650261962": {
- "message": "Sleep needs to pause %s",
+ "-648891906": {
+ "message": "Activity not running or entered PiP, resuming next.",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-641258376": {
"message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...",
@@ -1309,12 +1339,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-606328116": {
- "message": "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-597091183": {
"message": "Delete TaskDisplayArea uid=%d",
"level": "VERBOSE",
@@ -1375,11 +1399,11 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowAnimator.java"
},
- "-533690126": {
- "message": "resumeTopActivityLocked: Resumed %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "-542756093": {
+ "message": "TaskFragment vanished name=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
"-532081937": {
"message": " Commit activity becoming invisible: %s",
@@ -1387,11 +1411,11 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-527683022": {
- "message": "resumeTopActivityLocked: Skip resume: some activity pausing.",
+ "-521613870": {
+ "message": "App died during pause, not stopping: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-519504830": {
"message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s",
@@ -1477,18 +1501,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
- "-427457280": {
- "message": "App died while pausing: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
- "-417514857": {
- "message": "Key dispatch not paused for screen off",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@@ -1597,11 +1609,17 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
- "-303497363": {
- "message": "reparent: moving activity=%s to task=%d at %d",
+ "-312353598": {
+ "message": "Executing finish of activity: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "-310337305": {
+ "message": "Activity config changed during resume: %s, new next: %s",
"level": "INFO",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-302468788": {
"message": "Expected target rootTask=%s to be top most but found rootTask=%s",
@@ -1621,12 +1639,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-279436615": {
- "message": "Moving to PAUSING: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -1639,6 +1651,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-248761393": {
+ "message": "startPausing: taskFrag =%s mResumedActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-240296576": {
"message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
"level": "VERBOSE",
@@ -1651,12 +1669,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-234244777": {
- "message": "Activity config changed during resume: %s, new next: %s",
- "level": "INFO",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-230587670": {
"message": "SyncGroup %d: Unfinished container: %s",
"level": "VERBOSE",
@@ -1723,12 +1735,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-118786523": {
- "message": "Resume failed; resetting state to %s: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-116086365": {
"message": "******************** ENABLING SCREEN!",
"level": "INFO",
@@ -1777,6 +1783,18 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/Session.java"
},
+ "-80004683": {
+ "message": "Resume failed; resetting state to %s: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "-55185509": {
+ "message": "setFocusedTask: taskId=%d touchedActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"-50336993": {
"message": "moveFocusableActivityToTop: activity=%s",
"level": "DEBUG",
@@ -1909,12 +1927,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowContextListenerController.java"
},
- "94402792": {
- "message": "Moving to RESUMED: %s (in existing)",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"95216706": {
"message": "hideIme target: %s ",
"level": "DEBUG",
@@ -1933,6 +1945,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "102618780": {
+ "message": "resumeTopActivity: Pausing %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"108170907": {
"message": "Add starting %s: startingData=%s",
"level": "VERBOSE",
@@ -2173,6 +2191,18 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "327461496": {
+ "message": "Complete pause: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "341055768": {
+ "message": "resumeTopActivity: Skip resume: need to start pausing",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2191,6 +2221,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "352982444": {
+ "message": " allReady query: used=%b override=%b states=[%s]",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"355720268": {
"message": "stopFreezingDisplayLocked: Unfreezing now",
"level": "DEBUG",
@@ -2227,11 +2263,11 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "391189028": {
- "message": "pauseBackTasks: task=%s mResumedActivity=%s",
- "level": "DEBUG",
+ "378825104": {
+ "message": "Enqueueing pending pause: %s",
+ "level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"397105698": {
"message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
@@ -2365,6 +2401,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
+ "573582981": {
+ "message": "reparent: moving activity=%s to new task fragment in task=%d at %d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"579298675": {
"message": "Moving to DESTROYED: %s (removed from history)",
"level": "VERBOSE",
@@ -2467,6 +2509,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "660908897": {
+ "message": "Auto-PIP allowed, entering PIP mode directly: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"662572728": {
"message": "Attempted to add a toast window with bad token %s. Aborting.",
"level": "WARN",
@@ -2485,12 +2533,24 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "669361121": {
+ "message": "Sleep still need to stop %d activities",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"674932310": {
"message": "Setting Intent of %s to target %s",
"level": "VERBOSE",
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "675705156": {
+ "message": "resumeTopActivity: Top activity resumed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"685047360": {
"message": "Resizing window %s",
"level": "VERBOSE",
@@ -2521,12 +2581,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "709500946": {
- "message": "resumeTopActivityLocked: Skip resume: need to start pausing",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"715749922": {
"message": "Allowlisting %d:%s",
"level": "WARN",
@@ -2629,12 +2683,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "897964776": {
- "message": "Complete pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"898863925": {
"message": "Attempted to add QS dialog window with unknown token %s. Aborting.",
"level": "WARN",
@@ -2659,6 +2707,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
+ "935418348": {
+ "message": "resumeTopActivity: Skip resume: some activity pausing.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"950074526": {
"message": "setLockTaskMode: Can't lock due to auth",
"level": "WARN",
@@ -2707,11 +2761,11 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "988389910": {
- "message": "resumeTopActivityLocked: Pausing %s",
- "level": "DEBUG",
+ "987903142": {
+ "message": "Sleep needs to pause %s",
+ "level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"996960396": {
"message": "Starting Transition %d",
@@ -2719,18 +2773,24 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "1001509841": {
- "message": "Auto-PIP allowed, entering PIP mode directly: %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1001904964": {
"message": "***** BOOT TIMEOUT: forcing display enabled",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1011462000": {
+ "message": "Re-launching after pause: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "1022095595": {
+ "message": "TaskFragment info changed name=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1023413388": {
"message": "Finish waiting for pause of: %s",
"level": "VERBOSE",
@@ -2917,6 +2977,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1284122013": {
+ "message": "TaskFragment appeared name=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1288731814": {
"message": "WindowState.hideLw: setting mFocusMayChange true",
"level": "INFO",
@@ -3163,12 +3229,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
- "1585450696": {
- "message": "resumeTopActivityLocked: Restarting %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1589610525": {
"message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s",
"level": "VERBOSE",
@@ -3217,6 +3277,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "1653025361": {
+ "message": "Register task fragment organizer=%s uid=%d pid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1653210583": {
"message": "Removing app %s delayed=%b animation=%s animating=%b",
"level": "VERBOSE",
@@ -3229,6 +3295,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/InsetsStateController.java"
},
+ "1670933628": {
+ "message": " Setting allReady override",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"1671994402": {
"message": "Nulling last startingData",
"level": "VERBOSE",
@@ -3385,18 +3457,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1837992242": {
- "message": "Executing finish of activity: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
- "1847414670": {
- "message": "Activity not running or entered PiP, resuming next.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1853793312": {
"message": "Notify removed startingWindow %s",
"level": "VERBOSE",
@@ -3409,6 +3469,12 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1856783490": {
+ "message": "resumeTopActivity: Restarting %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1865125884": {
"message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b",
"level": "DEBUG",
@@ -3421,30 +3487,24 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1884961873": {
- "message": "Sleep still need to stop %d activities",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "1894239744": {
- "message": "Enqueueing pending pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1903353011": {
"message": "notifyAppStopped: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1912291550": {
+ "message": "Sleep still waiting to pause %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1918448345": {
"message": "Task appeared taskId=%d",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0171_Product_0419.kl b/data/keyboards/Vendor_0171_Product_0419.kl
new file mode 100644
index 000000000000..05a25f0210c8
--- /dev/null
+++ b/data/keyboards/Vendor_0171_Product_0419.kl
@@ -0,0 +1,55 @@
+# 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.
+
+#
+# Amazon Luna Controller
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x02 Z flat 4096
+axis 0x05 RZ flat 4096
+
+# Triggers.
+axis 0x0a LTRIGGER
+axis 0x09 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Action button (circle icon, left of the Home button)
+key 158 BUTTON_SELECT
+
+# Home button (branded button in the center of the controller)
+key 172 BUTTON_MODE
+
+# Menu button (hamburger icon, right of the Home button)
+key 315 BUTTON_START
+
+# Alexa Push-To-Talk button (microphone icon, below the Home button)
+key 217 MEDIA_RECORD
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
new file mode 100644
index 000000000000..3d7b94f880ac
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
@@ -0,0 +1,153 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkUnattributedNoteOpCall",
+ summary = "Verifies that a noteOp() call is attributed",
+ severity = WARNING)
+public final class UnattributedNoteOpCallChecker extends BugChecker
+ implements MethodInvocationTreeMatcher {
+
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOpNoThrow(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOpNoThrow(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_4 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int,int,java.lang.String,boolean)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(int,int,java.lang.String,boolean)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOp(java.lang.String,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOp(int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String,int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(int,int,java.lang.String)"));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (UNATTRIBUTED_NOTEOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_NOTEOP_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteOp call! Please use noteOp(int, String, String, String) or noteOp(int, CallerIdentity)")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEOPNOTHROW_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteOpNoThrow call! Please use noteOpNoThrow(String, int, String, String, String)")
+ .build();
+ }
+ if (UNATTRIBUTED_STARTOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_3.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_4.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed startOp call! Please use startOp(int, int, String, boolean, String, String)")
+ .build();
+ }
+ if (UNATTRIBUTED_STARTOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_STARTOPNOTHROW_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_STARTOPNOTHROW_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed startOpNoThrow call!")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEPROXYOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEPROXYOP_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteProxyOp call!")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteProxyOpNoThrow call!")
+ .build();
+ }
+ if (UNATTRIBUTED_FINISHOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_FINISHOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_FINISHOP_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed finishOp call!")
+ .build();
+ }
+
+
+
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
new file mode 100644
index 000000000000..9a98c7c2d4c9
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UnattributedNoteOpCallCheckerTest {
+ private CompilationTestHelper mCompilationHelper;
+
+ @Before
+ public void setUp() {
+ mCompilationHelper = CompilationTestHelper.newInstance(
+ UnattributedNoteOpCallChecker.class, getClass());
+ }
+
+ @Test
+ public void testNoteOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " mAppOps.noteOp(\"foo\", 0, \"bar\", \"baz\", \"qux\");",
+ " mAppOps.noteOp(0, 0, \"bar\", \"baz\", \"qux\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOpNoThrow(0, 1, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOpNoThrow(\"foo\", 1, \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStartOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(0, 0, \"bar\", true);",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStartOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(0, 0, \"bar\", true);",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(\"foo\", 1, \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteProxyOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOp(1, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOp(\"foo\", \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteProxyOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\", 1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testFinishOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+
+
+}
diff --git a/errorprone/tests/res/android/app/AppOpsManager.java b/errorprone/tests/res/android/app/AppOpsManager.java
new file mode 100644
index 000000000000..216270cb65d7
--- /dev/null
+++ b/errorprone/tests/res/android/app/AppOpsManager.java
@@ -0,0 +1,107 @@
+/*
+ * 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.app;
+
+public class AppOpsManager {
+
+ public int noteOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(String op, int uid, String packageName,
+ String attributionTag, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op, int uid, String packageName,
+ String attributionTag, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOpNoThrow(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOpNoThrow(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOp(String op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOp(int op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOpNoThrow(String op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOpNoThrow(String op, String proxiedPackageName,
+ int proxiedUid) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 93a336e7a408..96b33259e739 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,7 @@ import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.os.LocaleList;
import android.text.FontConfig;
+import android.util.ArraySet;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -37,6 +38,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -120,7 +122,23 @@ public class FontListParser {
}
}
- private static FontConfig readFamilies(
+ /**
+ * Parses the familyset tag in font.xml
+ * @param parser a XML pull parser
+ * @param fontDir A system font directory, e.g. "/system/fonts"
+ * @param customization A OEM font customization
+ * @param updatableFontMap A map of updated font files
+ * @param lastModifiedDate A date that the system font is updated.
+ * @param configVersion A version of system font config.
+ * @param allowNonExistingFile true if allowing non-existing font files during parsing fonts.xml
+ * @return result of fonts.xml
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ *
+ * @hide
+ */
+ public static FontConfig readFamilies(
@NonNull XmlPullParser parser,
@NonNull String fontDir,
@NonNull FontCustomizationParser.Result customization,
@@ -159,7 +177,24 @@ public class FontListParser {
}
families.addAll(oemNamedFamilies.values());
- return new FontConfig(families, aliases, lastModifiedDate, configVersion);
+
+ // Filters aliases that point to non-existing families.
+ Set<String> namedFamilies = new ArraySet<>();
+ for (int i = 0; i < families.size(); ++i) {
+ String name = families.get(i).getName();
+ if (name != null) {
+ namedFamilies.add(name);
+ }
+ }
+ List<FontConfig.Alias> filtered = new ArrayList<>();
+ for (int i = 0; i < aliases.size(); ++i) {
+ FontConfig.Alias alias = aliases.get(i);
+ if (namedFamilies.contains(alias.getOriginal())) {
+ filtered.add(alias);
+ }
+ }
+
+ return new FontConfig(families, filtered, lastModifiedDate, configVersion);
}
private static boolean keepReading(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index fe04f0dd4c83..187ddd892034 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1106,6 +1106,43 @@ public class HardwareRenderer {
ProcessInitializer.sInstance.setContext(context);
}
+ /**
+ * Returns true if HardwareRender will produce output.
+ *
+ * This value is global to the process and affects all uses of HardwareRenderer,
+ * including
+ * those created by the system such as those used by the View tree when using hardware
+ * accelerated rendering.
+ *
+ * Default is true in all production environments, but may be false in testing-focused
+ * emulators or if {@link #setDrawingEnabled(boolean)} is used.
+ */
+ public static boolean isDrawingEnabled() {
+ return nIsDrawingEnabled();
+ }
+
+ /**
+ * Toggles whether or not HardwareRenderer will produce drawing output globally in the current
+ * process.
+ *
+ * This applies to all HardwareRenderer instances, including those created by the platform such
+ * as those used by the system for hardware accelerated View rendering.
+ *
+ * The capability to disable drawing output is intended for test environments, primarily
+ * headless ones. By setting this to false, tests that launch activities or interact with Views
+ * can be quicker with less RAM usage by skipping the final step of View drawing. All View
+ * lifecycle events will occur as normal, only the final step of rendering on the GPU to the
+ * display will be skipped.
+ *
+ * This can be toggled on and off at will, so screenshot tests can also run in this same
+ * environment by toggling drawing back on and forcing a frame to be drawn such as by calling
+ * view#invalidate(). Once drawn and the screenshot captured, this can then be turned back off.
+ */
+ // TODO: Add link to androidx's Screenshot library for help with this
+ public static void setDrawingEnabled(boolean drawingEnabled) {
+ nSetDrawingEnabled(drawingEnabled);
+ }
+
private static final class DestroyContextRunnable implements Runnable {
private final long mNativeInstance;
@@ -1438,4 +1475,8 @@ public class HardwareRenderer {
private static native void nInitDisplayInfo(int width, int height, float refreshRate,
int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
+
+ private static native void nSetDrawingEnabled(boolean drawingEnabled);
+
+ private static native boolean nIsDrawingEnabled();
}
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
index a1a7aa85519f..053bec74405e 100644
--- a/keystore/java/android/security/GenerateRkpKey.java
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -22,6 +22,10 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner
@@ -41,14 +45,25 @@ import android.os.RemoteException;
* @hide
*/
public class GenerateRkpKey {
+ private static final String TAG = "GenerateRkpKey";
+
+ private static final int NOTIFY_EMPTY = 0;
+ private static final int NOTIFY_KEY_GENERATED = 1;
+ private static final int TIMEOUT_MS = 1000;
private IGenerateRkpKeyService mBinder;
private Context mContext;
+ private CountDownLatch mCountDownLatch;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = IGenerateRkpKeyService.Stub.asInterface(service);
+ mCountDownLatch.countDown();
+ }
+
+ @Override public void onBindingDied(ComponentName className) {
+ mCountDownLatch.countDown();
}
@Override
@@ -64,36 +79,52 @@ public class GenerateRkpKey {
mContext = context;
}
- /**
- * Fulfills the use case of (2) described in the class documentation. Blocks until the
- * RemoteProvisioner application can get new attestation keys signed by the server.
- */
- public void notifyEmpty(int securityLevel) throws RemoteException {
+ private void bindAndSendCommand(int command, int securityLevel) throws RemoteException {
Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ if (comp == null) {
+ // On a system that does not use RKP, the RemoteProvisioner app won't be installed.
+ return;
+ }
intent.setComponent(comp);
- if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
- throw new RemoteException("Failed to bind to GenerateKeyService");
+ mCountDownLatch = new CountDownLatch(1);
+ if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ throw new RemoteException("Failed to bind to GenerateRkpKeyService");
+ }
+ try {
+ mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted: ", e);
}
if (mBinder != null) {
- mBinder.generateKey(securityLevel);
+ switch (command) {
+ case NOTIFY_EMPTY:
+ mBinder.generateKey(securityLevel);
+ break;
+ case NOTIFY_KEY_GENERATED:
+ mBinder.notifyKeyGenerated(securityLevel);
+ break;
+ default:
+ Log.e(TAG, "Invalid case for command");
+ }
+ } else {
+ Log.e(TAG, "Binder object is null; failed to bind to GenerateRkpKeyService.");
}
mContext.unbindService(mConnection);
}
/**
- * FUlfills the use case of (1) described in the class documentation. Non blocking call.
+ * Fulfills the use case of (2) described in the class documentation. Blocks until the
+ * RemoteProvisioner application can get new attestation keys signed by the server.
+ */
+ public void notifyEmpty(int securityLevel) throws RemoteException {
+ bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
+ }
+
+ /**
+ * Fulfills the use case of (1) described in the class documentation. Non blocking call.
*/
public void notifyKeyGenerated(int securityLevel) throws RemoteException {
- Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
- throw new RemoteException("Failed to bind to GenerateKeyService");
- }
- if (mBinder != null) {
- mBinder.notifyKeyGenerated(securityLevel);
- }
- mContext.unbindService(mConnection);
+ bindAndSendCommand(NOTIFY_KEY_GENERATED, securityLevel);
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 7258a3af9e68..2e85b304ec47 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -599,7 +599,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
} catch (RemoteException e) {
// This is not really an error state, and necessarily does not apply to non RKP
// systems or hybrid systems where RKP is not currently turned on.
- Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.");
+ Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.", e);
}
success = true;
return new KeyPair(publicKey, publicKey.getPrivateKey());
diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml
new file mode 100644
index 000000000000..4e2a77f213a0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_outline.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<com.android.wm.shell.splitscreen.OutlineRoot
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.wm.shell.splitscreen.OutlineView
+ android:id="@+id/split_outline"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
+</com.android.wm.shell.splitscreen.OutlineRoot>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index ea634cfa907c..69aa31ee861a 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Slaan oor na volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Slaan oor na vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Verander grootte"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Hou vas"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Program sal dalk nie met verdeelde skerm werk nie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+ <string name="got_it" msgid="4428750913636945527">"Het dit"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index e4628d7b5278..c754e3ca4dd2 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ወደ ቀጣይ ዝለል"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ወደ ቀዳሚ ዝለል"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"መጠን ይቀይሩ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+ <string name="got_it" msgid="4428750913636945527">"ገባኝ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 7b5bda72ccd4..ac72a3d7a367 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"إخفاء"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"إظهار"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+ <string name="got_it" msgid="4428750913636945527">"حسنًا"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 47294c438729..1ace3cdacb46 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"আকাৰ সলনি কৰক"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+ <string name="got_it" msgid="4428750913636945527">"বুজি পালোঁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 923ff79e0627..6d3e0a92d2ce 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Növbətiyə keçin"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Əvvəlkinə keçin"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ölçüsünü dəyişin"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Güvənli məkanda saxlayın"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aşağı tam ekran"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bir əlli rejimdən istifadə edilir"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Birəlli rejim istifadəsi"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bir əlli rejimi başladın"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bir əlli rejimdən çıxın"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Birəlli rejim başlasın"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Birəlli rejimdən çıxın"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> yumrucuqları üçün ayarlar"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kənara çıxma"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yenidən dəstəyə əlavə edin"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
+ <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 02e609cd5c9b..358da25d92c0 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavite u tajnu memoriju"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+ <string name="got_it" msgid="4428750913636945527">"Važi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index ccea3180f64e..7a934ccb1e9a 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перайсці да наступнага"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перайсці да папярэдняга"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змяніць памер"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Схаваць"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Праграма можа не працаваць у рэжыме падзеленага экрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+ <string name="got_it" msgid="4428750913636945527">"Зразумела"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index d29660b9c24d..02930b1db277 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Към следващия елемент"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Към предишния елемент"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Преоразмеряване"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Съхраняване"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Приложението може да не работи в режим на разделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+ <string name="got_it" msgid="4428750913636945527">"Разбрах"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 84bcaf907d91..b35e17919cc2 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"রিসাইজ করুন"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"স্ট্যাস করুন"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+ <string name="got_it" msgid="4428750913636945527">"বুঝেছি"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 85e08d7ca555..14d90a488352 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeći"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodni"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavljanje u stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi na podijeljenom ekranu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+ <string name="got_it" msgid="4428750913636945527">"Razumijem"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index a80b7fbec09a..9cffbdd7159e 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ves al següent"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Torna a l\'anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Canvia la mida"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Amaga"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entesos"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e8257bc8ee92..9b5206a1da5c 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Přeskočit na další"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Přeskočit na předchozí"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Změnit velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Uložit"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+ <string name="got_it" msgid="4428750913636945527">"Rozumím"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 17f8286e8069..a06abf10cb23 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Gå videre til næste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Gå til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Rediger størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skjul"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen fungerer muligvis ikke i opdelt skærm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index f04796aca753..c5e79f87ca50 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus nicht."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
@@ -60,13 +62,15 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf „Verwalten“, um Bubbles für diese App zu deaktivieren"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index cc329e8f3274..fc397c5569a8 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Μετάβαση στο επόμενο"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Μετάβαση στο προηγούμενο"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Αλλαγή μεγέθους"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Απόκρυψη"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+ <string name="got_it" msgid="4428750913636945527">"Το κατάλαβα"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 90c71c0e11ea..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 90c71c0e11ea..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 90c71c0e11ea..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 90c71c0e11ea..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index d8b5b40035f7..87210d5ecbec 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎Skip to next‎‏‎‎‏‎"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎Skip to previous‎‏‎‎‏‎"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎Resize‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎Stash‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎App may not work with split-screen.‎‏‎‎‏‎"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎App does not support split-screen.‎‏‎‎‏‎"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎Tap to restart this app and go full screen.‎‏‎‎‏‎"</string>
+ <string name="got_it" msgid="4428750913636945527">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎Got it‎‏‎‎‏‎"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 7244b1a1bcf5..ebe41e88f08c 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar el tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Almacenar de manera segura"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
@@ -58,7 +60,7 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbujas"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 65e75bde573d..5949099bd6a3 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Saltar al siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Volver al anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
@@ -60,7 +62,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca para abrir la burbuja. Arrastra para moverla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestionar para desactivar las burbujas de esta aplicación"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 0ccfcfee85d6..83309810311c 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Järgmise juurde"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Eelmise juurde"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Suuruse muutmine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Pane hoidlasse"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+ <string name="got_it" msgid="4428750913636945527">"Selge"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 6682ea80cf42..664976983fdc 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -25,9 +25,11 @@
<string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
<string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
<string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
- <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string>
- <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Joan aurrekora"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Gorde"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ez gorde"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ados"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index a41811d53357..f646039df1f8 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"رد شدن به بعدی"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"رد شدن به قبلی"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغییر اندازه"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"مخفی‌سازی"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفی‌سازی"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن است برنامه با «صفحهٔ دونیمه» کار نکند."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمی‌کند."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
@@ -43,7 +45,7 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"٪۳۰ بالا"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"تمام‌صفحه پایین"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از «حالت تک حرکت»"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از حالت یک‌دستی"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحه‌نمایش تند به‌طرف بالا بکشید یا در هر جایی از بالای برنامه که می‌خواهید ضربه بزنید"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"آغاز «حالت تک حرکت»"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت تک حرکت»"</string>
@@ -62,11 +64,13 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابک‌ها"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابک‌ها در هرزمانی"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابک‌ها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابک‌ها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌ها اخیر و حبابک‌ها ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌های اخیر و حبابک‌های ردشده اینجا ظاهر خواهند شد"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
+ <string name="got_it" msgid="4428750913636945527">"متوجه‌ام"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index fcdc70fc9cda..5f871639a202 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siirry seuraavaan"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Siirry edelliseen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Muuta kokoa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Lisää turvasäilytykseen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index ed822373e557..68df5917eab4 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Revenir au précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ajouter à la réserve"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index ad98b85d5d5d..eecc9cbbba43 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au contenu suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passer au contenu précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -61,7 +63,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôler les paramètres des bulles"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôlez les bulles à tout moment"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Appuyez sur \"Gérer\" pour désactiver les bulles de cette application"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 529825e68151..3583cafdfb9f 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ir ao seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ir ao anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Pode que a aplicación non funcione coa pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ee23e1e967ec..e0654bd6bfef 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"આગલા પર જાઓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"પહેલાંના પર જાઓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"કદ બદલો"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"છુપાવો"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"વિભાજિત-સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+ <string name="got_it" msgid="4428750913636945527">"સમજાઈ ગયું"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 34c1c85211f6..55a30f2358fb 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"अगले पर जाएं"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"पिछले पर जाएं"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदलें"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"छिपाएं"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"दिखाएं"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ऐप्लिकेशन शायद स्प्लिट स्क्रीन मोड में काम न करे."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ऐप विभाजित स्‍क्रीन का समर्थन नहीं करता है."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
@@ -67,6 +69,8 @@
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+ <string name="got_it" msgid="4428750913636945527">"ठीक है"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 32b21aadbb2f..f6acb5cb2d33 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sakrijte"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+ <string name="got_it" msgid="4428750913636945527">"Shvaćam"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 123b127bd5a3..0c1c8a40c8bf 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ugrás a következőre"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ugrás az előzőre"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Átméretezés"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Félretevés"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
+ <string name="got_it" msgid="4428750913636945527">"Rendben"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b047cf131aa8..36204c1a6599 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Անցնել հաջորդին"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Վերադառնալ նախորդին"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Փոխել չափը"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Թաքցնել"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում։"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+ <string name="got_it" msgid="4428750913636945527">"Եղավ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index a75cdb4b2b85..de962c43b137 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lewati ke berikutnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lewati ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah ukuran"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+ <string name="got_it" msgid="4428750913636945527">"Oke"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 3b28148e3171..c205d22e7ae8 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Fara á næsta"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Fara á fyrra"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Breyta stærð"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Geymsla"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Hugsanlega virkar forritið ekki með skjáskiptingu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ég skil"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 8a2b9dbd9ba8..c788a03a5b29 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passa ai contenuti successivi"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passa ai contenuti precedenti"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ridimensiona"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Accantona"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
@@ -60,7 +62,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 20114a7fa5f3..b0c03edf168d 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -18,16 +18,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"הרחב"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"הרחבה"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולהשבית את התכונה."</string>
<string name="pip_play" msgid="3496151081459417097">"הפעלה"</string>
- <string name="pip_pause" msgid="690688849510295232">"השהה"</string>
+ <string name="pip_pause" msgid="690688849510295232">"השהיה"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"אפשר לדלג אל הבא"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"אפשר לדלג אל הקודם"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"שינוי גודל"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"הסתרה זמנית"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ייתכן שהאפליקציה לא תפעל במסך מפוצל."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
@@ -41,14 +43,14 @@
<string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"מסך עליון מלא"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"עליון 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"עליון 50%"</string>
- <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"עליון 30%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"למעלה 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"מסך תחתון מלא"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש במצב שימוש ביד אחת"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש בתכונה \'מצב שימוש ביד אחת\'"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"כדי לצאת, יש להחליק למעלה מתחתית המסך או להקיש במקום כלשהו במסך מעל האפליקציה"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"הפעלה של מצב שימוש ביד אחת"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"יציאה ממצב שימוש ביד אחת"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות בשביל בועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"גלישה"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות לבועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"אפשרויות נוספות"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"הוספה בחזרה לערימה"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מהאפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מ-<xliff:g id="APP_NAME">%2$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+ <string name="got_it" msgid="4428750913636945527">"הבנתי"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 8ca54e0a5473..ef98a9c41cf2 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,6 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
- <string name="pip_close" msgid="9135220303720555525">"‏סגור PIP"</string>
+ <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fbb2951a06e1..36700bd81717 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"次へスキップ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"前へスキップ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"サイズ変更"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"非表示"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"アプリは分割画面では動作しないことがあります。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
@@ -61,7 +63,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"バブルはいつでも管理可能"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index f978481be23d..af1377a2f1e0 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"შემდეგზე გადასვლა"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"წინაზე გადასვლა"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ზომის შეცვლა"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"გადანახვა"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+ <string name="got_it" msgid="4428750913636945527">"გასაგებია"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 2d27fafcc98b..6deb0b892316 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Келесіге өту"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Алдыңғысына оралу"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлшемін өзгерту"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Жасыру"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
@@ -68,5 +70,7 @@
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+ <string name="got_it" msgid="4428750913636945527">"Түсінікті"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index d503b7a5edca..c59d0fc4f60d 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"រំលងទៅបន្ទាប់"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"រំលងទៅក្រោយ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ប្ដូរ​ទំហំ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"លាក់ជាបណ្ដោះអាសន្ន"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"កម្មវិធី​អាចនឹងមិន​ដំណើរការ​ជាមួយ​មុខងារបំបែកអេក្រង់​ទេ។"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
+ <string name="got_it" msgid="4428750913636945527">"យល់ហើយ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 3d61d84f4810..5e655b4c7bee 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ಮುಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ಹಿಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್‌ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ವಿಭಜಿಸಿದ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="got_it" msgid="4428750913636945527">"ಅರ್ಥವಾಯಿತು"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index ea7ad56bf9d2..af34ef48cb31 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"다음으로 건너뛰기"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"이전으로 건너뛰기"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"크기 조절"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"숨기기"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+ <string name="got_it" msgid="4428750913636945527">"확인"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 611b2d60a8c1..c16041d87149 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Кийинкисине өткөрүп жиберүү"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Мурункусуна өткөрүп жиберүү"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлчөмүн өзгөртүү"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сейфке салуу"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
@@ -62,11 +64,13 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+ <string name="got_it" msgid="4428750913636945527">"Түшүндүм"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a1c998c078de..a578b0a3f4cd 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ຂ້າມໄປລາຍການໜ້າ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ຂ້າມໄປລາຍການກ່ອນນີ້"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ປ່ຽນຂະໜາດ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ເກັບໄວ້ບ່ອນເກັບສ່ວນຕົວ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+ <string name="got_it" msgid="4428750913636945527">"ເຂົ້າໃຈແລ້ວ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index b2ccd5709e21..e037839a54db 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Praleisti ir eiti į kitą"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Praleisti ir eiti į ankstesnį"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Pakeisti dydį"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslėpti"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Programa gali neveikti naudojant išskaidyto ekrano režimą."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+ <string name="got_it" msgid="4428750913636945527">"Supratau"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index e6d0c7725bbf..05472e454f77 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pāriet uz nākamo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pāriet uz iepriekšējo"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Mainīt lielumu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslēpt"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+ <string name="got_it" msgid="4428750913636945527">"Labi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 43f2881fd553..9cb2c6906c70 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Прескокни до следната"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Прескокни до претходната"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промени големина"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сокријте"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликацијата може да не работи со поделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+ <string name="got_it" msgid="4428750913636945527">"Сфатив"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index e675861166a3..f0bf513e264c 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"സ്റ്റാഷ് ചെയ്യൽ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"അൺസ്റ്റാഷ് ചെയ്യൽ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"സ്‌ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
@@ -66,7 +68,9 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
- <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="got_it" msgid="4428750913636945527">"മനസ്സിലായി"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 044fd9fa7544..68822cb4f51b 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Дараагийн медиад очих"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Өмнөх медиад очих"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Хэмжээг өөрчлөх"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Нуух"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апп хуваагдсан дэлгэц дээр ажиллахгүй байж болзошгүй."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ойлголоо"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index e838cf59331e..a4b7be4d52c7 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"डावलून पुढे जा"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"डावलून मागे जा"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदला"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्टॅश करा"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+ <string name="got_it" msgid="4428750913636945527">"समजले"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 6664f38f3879..2f33bfa41d83 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Langkau ke seterusnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Langkau ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah saiz"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sembunyikan"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 9681d14a6a88..018ffc02556d 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"နောက်တစ်ခုသို့ ကျော်ရန်"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ယခင်တစ်ခုသို့ ပြန်သွားရန်"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"အရွယ်အစားပြောင်းရန်"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"သိုဝှက်ရန်"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"မသိုဝှက်ရန်"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းဖြင့် အက်ပ်သည် အလုပ်မလုပ်ပါ။"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+ <string name="got_it" msgid="4428750913636945527">"ရပြီ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 986e890dfe3a..a23ad9068ea9 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hopp til neste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hopp til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Endre størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Oppbevar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+ <string name="got_it" msgid="4428750913636945527">"Greit"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 0369c6dd2831..5b9b87205428 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -28,9 +28,11 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"अर्कोमा जानुहोस्"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"अघिल्लोमा जानुहोस्"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्ट्यास गर्नुहोस्"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
- <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+ <string name="got_it" msgid="4428750913636945527">"बुझेँ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 26c276e7e690..06aaad7d65ca 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -22,12 +22,14 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en schakel je de functie uit."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en zet je de functie uit."</string>
<string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
<string name="pip_pause" msgid="690688849510295232">"Onderbreken"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Doorgaan naar volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Teruggaan naar vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Formaat aanpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Verbergen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"De app werkt mogelijk niet met gesplitst scherm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met één hand gebruiken"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met 1 hand gebruiken"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met één hand starten"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met één hand afsluiten"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met 1 hand starten"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met 1 hand afsluiten"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellingen voor <xliff:g id="APP_NAME">%1$s</xliff:g>-bubbels"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overloop"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Weer toevoegen aan stack"</string>
@@ -58,15 +60,17 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te zetten"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 27f16226a421..ac1e84a6e283 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ରିସାଇଜ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ଲୁଚାନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରିନରେ ଆପ୍ କାମ କରିନପାରେ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <string name="got_it" msgid="4428750913636945527">"ବୁଝିଗଲି"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 96688b952d66..bf5b733c22ea 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ਅਗਲੇ \'ਤੇ ਜਾਓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ਪਿਛਲੇ \'ਤੇ ਜਾਓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ਆਕਾਰ ਬਦਲੋ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ਸਟੈਸ਼"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+ <string name="got_it" msgid="4428750913636945527">"ਸਮਝ ਲਿਆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 6b640b54f898..cd659ba86319 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Dalej"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Wstecz"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmień rozmiar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Przenieś do schowka"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacja może nie działać przy podzielonym ekranie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 465d2d17a5e7..9c97ffe2d7e6 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index df841bf3eda4..1f5b0abd80d9 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Mudar para o anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Armazenar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"A app pode não funcionar com o ecrã dividido."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 465d2d17a5e7..9c97ffe2d7e6 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 55a437668b22..d694be1cdd18 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stocați"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulați stocarea"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 8ae00d28f896..e9bfffbad399 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти к следующему"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти к предыдущему"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Изменить размер"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Скрыть"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"В режиме разделения экрана приложение может работать нестабильно."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+ <string name="got_it" msgid="4428750913636945527">"ОК"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 081926fd101b..ba178f03efbb 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ඊළඟ එකට පනින්න"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"පෙර එකට පනින්න"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ප්‍රතිප්‍රමාණ කරන්න"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"සඟවා තබන්න"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"යෙදුම බෙදුම් තිරය සමග ක්‍රියා නොකළ හැකිය"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+ <string name="got_it" msgid="4428750913636945527">"තේරුණා"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 24fded7ebb04..e048ca15b91f 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskočiť na ďalšie"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskočiť na predchádzajúce"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmeniť veľkosť"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skryť"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikácia nemusí fungovať s rozdelenou obrazovkou."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+ <string name="got_it" msgid="4428750913636945527">"Dobre"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 3f425302a5ac..ed05908c6e03 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na naslednjega"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prejšnjega"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Spremeni velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Zakrij"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+ <string name="got_it" msgid="4428750913636945527">"Razumem"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index ddae724e7569..13e830cbd100 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Kalo te tjetra"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Kalo tek e mëparshmja"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ndrysho përmasat"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Fshih"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
+ <string name="got_it" msgid="4428750913636945527">"E kuptova"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 74c9ac0867e3..be6857b1f1a8 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Пређи на следеће"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Пређи на претходно"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промените величину"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ставите у тајну меморију"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+ <string name="got_it" msgid="4428750913636945527">"Важи"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 81328a836345..e61e69b51c5f 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hoppa till nästa"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hoppa till föregående"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ändra storlek"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Utför stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen kanske inte fungerar med delad skärm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 4559832b1d85..476af11cd3ad 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ruka ufikie inayofuata"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ruka ufikie iliyotangulia"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Badilisha ukubwa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ficha"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Fichua"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+ <string name="got_it" msgid="4428750913636945527">"Nimeelewa"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 586ee94a1098..bc27389c6116 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"அடுத்ததற்குச் செல்"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"முந்தையதற்குச் செல்"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"அளவு மாற்று"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"திரைப் பிரிப்பு அம்சத்தில் ஆப்ஸ் செயல்படாமல் போகக்கூடும்."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+ <string name="got_it" msgid="4428750913636945527">"சரி"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 4e85b4371220..624b8b37a1ee 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"పరిమాణం మార్చు"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్‌లోకి వెళ్లండి."</string>
+ <string name="got_it" msgid="4428750913636945527">"అర్థమైంది"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 66c701812ce8..9017b3f6b326 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ข้ามไปรายการถัดไป"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ข้ามไปรายการก่อนหน้า"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ปรับขนาด"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"เก็บเข้าที่เก็บส่วนตัว"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"แอปอาจใช้ไม่ได้กับโหมดแบ่งหน้าจอ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+ <string name="got_it" msgid="4428750913636945527">"รับทราบ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index a76bf6f1350c..c484cafb191a 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lumaktaw sa susunod"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lumaktaw sa nakaraan"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"I-resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"I-stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Posibleng hindi gumana ang app sa split screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index b3276dad50e7..ca856a1fe93b 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Sonrakine atla"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Öncekine atla"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Yeniden boyutlandır"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Depola"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
+ <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 8e303cf45a39..08e8d2942d57 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти далі"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти назад"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змінити розмір"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сховати"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показати"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Додаток може не працювати в режимі розділеного екрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Додаток не підтримує розділення екрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+ <string name="got_it" msgid="4428750913636945527">"Зрозуміло"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 4b0adc640ddd..06c09276e717 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+ <string name="got_it" msgid="4428750913636945527">"سمجھ آ گئی"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 74b135d44522..6a873a3e185e 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Keyingisiga o‘tish"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Avvalgisiga qaytish"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Oʻlchamini oʻzgartirish"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Berkitish"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Bu ilova ekranni ikkiga ajratish rejimini dastaklamaydi."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index ce372317b0b8..4d4eebcfd68b 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Chuyển tới mục tiếp theo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Chuyển về mục trước"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Đổi kích thước"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ẩn"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
+ <string name="got_it" msgid="4428750913636945527">"Tôi hiểu"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 3143130fa4ce..3b8c8894c4e7 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一个"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一个"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"调整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"隐藏"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"应用可能无法在分屏模式下正常运行。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+ <string name="got_it" msgid="4428750913636945527">"知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 4f8bfe016f6f..9ba82b5ddf72 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"保護"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+ <string name="got_it" msgid="4428750913636945527">"知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 6fb8ed963ba7..aa666531996d 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"暫時隱藏"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
@@ -44,7 +46,7 @@
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"以 30% 的螢幕空間顯示頂端畫面"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"以全螢幕顯示底部畫面"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"使用單手模式"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上的任何位置"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上方的任何位置"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"啟動單手模式"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"結束單手模式"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」對話框的設定"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+ <string name="got_it" msgid="4428750913636945527">"我知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index cab277647d26..c8199c844d5b 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Yeqela kokulandelayo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Yeqela kokwangaphambilini"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Shintsha usayizi"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Yenza isiteshi"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ngiyezwa"</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 34c66a4f4b82..bf074b0337ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -97,6 +97,14 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
b.setParent(sc);
}
+ public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) {
+ final SurfaceControl sc = mLeashes.get(displayId);
+ if (sc == null) {
+ throw new IllegalArgumentException("can't find display" + displayId);
+ }
+ tx.setPosition(sc, x, y);
+ }
+
@Override
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
@NonNull SurfaceControl leash) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 0b941b59b3db..9113c79d40f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -103,6 +103,8 @@ public final class ShellCommandHandlerImpl {
return runMoveToSideStage(args, pw);
case "removeFromSideStage":
return runRemoveFromSideStage(args, pw);
+ case "setSideStageOutline":
+ return runSetSideStageOutline(args, pw);
case "setSideStagePosition":
return runSetSideStagePosition(args, pw);
case "setSideStageVisibility":
@@ -161,6 +163,18 @@ public final class ShellCommandHandlerImpl {
return true;
}
+ private boolean runSetSideStageOutline(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First arguments are "WMShell" and command name.
+ pw.println("Error: whether to enable or disable side stage outline border should be"
+ + " provided as arguments");
+ return false;
+ }
+ final boolean enable = new Boolean(args[2]);
+ mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable));
+ return true;
+ }
+
private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
@@ -175,7 +189,7 @@ public final class ShellCommandHandlerImpl {
private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
- pw.println("Error: side stage position should be provided as arguments");
+ pw.println("Error: side stage visibility should be provided as arguments");
return false;
}
final Boolean visible = new Boolean(args[2]);
@@ -197,6 +211,8 @@ public final class ShellCommandHandlerImpl {
pw.println(" Move a task with given id in split-screen mode.");
pw.println(" removeFromSideStage <taskId>");
pw.println(" Remove a task with given id in split-screen mode.");
+ pw.println(" setSideStageOutline <true/false>");
+ pw.println(" Enable/Disable outline on the side-stage.");
pw.println(" setSideStagePosition <SideStagePosition>");
pw.println(" Sets the position of the side-stage.");
pw.println(" setSideStageVisibility <true/false>");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 1861e48482b8..2f3214d1d1ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -40,6 +40,8 @@ import android.view.ViewTreeObserver;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -74,6 +76,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -89,11 +92,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
- public TaskView(Context context, ShellTaskOrganizer organizer) {
+ public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mShellExecutor = organizer.getExecutor();
+ mSyncQueue = syncQueue;
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
@@ -189,8 +193,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mTaskToken, mTmpRect);
- // TODO(b/151449487): Enable synchronization
- mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.queue(wct);
}
/**
@@ -236,14 +239,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private void updateTaskVisibility() {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
- mTaskOrganizer.applyTransaction(wct);
- // TODO(b/151449487): Only call callback once we enable synchronization
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
+ mSyncQueue.queue(wct);
+ if (mListener == null) {
+ return;
+ }
+ int taskId = mTaskInfo.taskId;
+ mSyncQueue.runInSync((t) -> {
mListenerExecutor.execute(() -> {
mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
});
- }
+ });
}
@Override
@@ -264,10 +269,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
updateTaskVisibility();
}
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
- // TODO: Synchronize show with the resize
onLocationChanged();
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
+ mSyncQueue.runInSync((t) -> {
+ setResizeBackgroundColor(t, backgroundColor);
+ });
}
if (mListener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 58ca1fbaba24..8286d102791e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -20,8 +20,8 @@ import android.annotation.UiContext;
import android.content.Context;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -30,12 +30,14 @@ import java.util.function.Consumer;
public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
- ShellExecutor shellExecutor) {
+ ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
mTaskOrganizer = taskOrganizer;
mShellExecutor = shellExecutor;
+ mSyncQueue = syncQueue;
}
public TaskViewFactory asTaskViewFactory() {
@@ -44,7 +46,7 @@ public class TaskViewFactoryController {
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer);
+ TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 09fcb86e56de..f3f66dc894ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -85,6 +85,7 @@ import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -137,6 +138,7 @@ public class BubbleController {
private final TaskStackListenerImpl mTaskStackListener;
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
+ private final SyncTransactionQueue mSyncQueue;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -209,7 +211,8 @@ public class BubbleController {
ShellTaskOrganizer organizer,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
@@ -217,7 +220,7 @@ public class BubbleController {
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler);
+ mainHandler, syncQueue);
}
/**
@@ -239,7 +242,8 @@ public class BubbleController {
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
mContext = context;
mLauncherApps = launcherApps;
mBarService = statusBarService == null
@@ -262,6 +266,7 @@ public class BubbleController {
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
mDisplayController = displayController;
+ mSyncQueue = syncQueue;
}
public void initialize() {
@@ -561,6 +566,10 @@ public class BubbleController {
return mTaskOrganizer;
}
+ SyncTransactionQueue getSyncTransactionQueue() {
+ return mSyncQueue;
+ }
+
/** Contains information to help position things on the screen. */
BubblePositioner getPositioner() {
return mBubblePositioner;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 9687ec6a8168..a02fa9b18e49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -25,6 +25,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -60,7 +61,6 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import com.android.wm.shell.TaskView;
import com.android.wm.shell.common.AlphaOptimizedButton;
@@ -77,7 +77,6 @@ public class BubbleExpandedView extends LinearLayout {
// The triangle pointing to the expanded view
private View mPointerView;
- private int mPointerMargin;
@Nullable private int[] mExpandedViewContainerLocation;
private AlphaOptimizedButton mManageButton;
@@ -102,9 +101,6 @@ public class BubbleExpandedView extends LinearLayout {
*/
private boolean mIsAlphaAnimating = false;
- private int mMinHeight;
- private int mOverflowHeight;
- private int mManageButtonHeight;
private int mPointerWidth;
private int mPointerHeight;
private float mPointerRadius;
@@ -338,7 +334,8 @@ public class BubbleExpandedView extends LinearLayout {
bringChildToFront(mOverflowView);
mManageButton.setVisibility(GONE);
} else {
- mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
+ mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
+ mController.getSyncTransactionQueue());
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
@@ -347,12 +344,8 @@ public class BubbleExpandedView extends LinearLayout {
void updateDimensions() {
Resources res = getResources();
- mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
- mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
-
updateFontSize();
- mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerRadius = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_radius);
@@ -368,7 +361,6 @@ public class BubbleExpandedView extends LinearLayout {
updatePointerView();
}
- mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
if (mManageButton != null) {
int visibility = mManageButton.getVisibility();
removeView(mManageButton);
@@ -632,12 +624,11 @@ public class BubbleExpandedView extends LinearLayout {
}
if ((mBubble != null && mTaskView != null) || mIsOverflow) {
- float desiredHeight = mIsOverflow
- ? mPositioner.isLargeScreen() ? getMaxExpandedHeight() : mOverflowHeight
- : mBubble.getDesiredHeight(mContext);
- desiredHeight = Math.max(desiredHeight, mMinHeight);
- float height = Math.min(desiredHeight, getMaxExpandedHeight());
- height = Math.max(height, mMinHeight);
+ float desiredHeight = mPositioner.getExpandedViewHeight(mBubble);
+ int maxHeight = mPositioner.getMaxExpandedViewHeight(mIsOverflow);
+ float height = desiredHeight == MAX_HEIGHT
+ ? maxHeight
+ : Math.min(desiredHeight, maxHeight);
FrameLayout.LayoutParams lp = mIsOverflow
? (FrameLayout.LayoutParams) mOverflowView.getLayoutParams()
: (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
@@ -661,23 +652,6 @@ public class BubbleExpandedView extends LinearLayout {
}
}
- private int getMaxExpandedHeight() {
- int expandedContainerY = mExpandedViewContainerLocation != null
- // Remove top insets back here because availableRect.height would account for that
- ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top
- : 0;
- int settingsHeight = mIsOverflow ? 0 : mManageButtonHeight;
- int pointerHeight = mPositioner.showBubblesVertically()
- ? mPointerWidth
- : (int) (mPointerHeight - mPointerOverlap + mPointerMargin);
- return mPositioner.getAvailableRect().height()
- - expandedContainerY
- - getPaddingTop()
- - getPaddingBottom()
- - settingsHeight
- - pointerHeight;
- }
-
/**
* Update appearance of the expanded view being displayed.
*
@@ -727,14 +701,11 @@ public class BubbleExpandedView extends LinearLayout {
: mPointerHeight - mPointerOverlap;
setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0);
- final float expandedViewY = mPositioner.getExpandedViewY();
- // TODO: I don't understand why it works but it does - why normalized in portrait
- // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
- final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
- mPositioner.getBubbleSize());
- final float bubbleCenter = showVertically
- ? bubblePosition + (mPositioner.getBubbleSize() / 2f) - expandedViewY
- : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
+ // Subtract the expandedViewY here because the pointer is placed within the expandedView.
+ float pointerPosition = mPositioner.getPointerPosition(bubblePosition);
+ final float bubbleCenter = mPositioner.showBubblesVertically()
+ ? pointerPosition - mPositioner.getExpandedViewY(mBubble, bubblePosition)
+ : pointerPosition;
// Post because we need the width of the view
post(() -> {
float pointerY;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c600f56ba0c5..0a856a8231a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -34,6 +34,7 @@ import android.view.WindowMetrics;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import java.lang.annotation.Retention;
@@ -58,6 +59,8 @@ public class BubblePositioner {
/** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
public static final int NUM_VISIBLE_WHEN_RESTING = 2;
+ /** Indicates a bubble's height should be the maximum available space. **/
+ public static final int MAX_HEIGHT = -1;
private Context mContext;
private WindowManager mWindowManager;
@@ -68,13 +71,16 @@ public class BubblePositioner {
private int mMaxBubbles;
private int mBubbleSize;
- private int mBubbleBadgeSize;
private int mSpacingBetweenBubbles;
private int mExpandedViewLargeScreenWidth;
private int mExpandedViewPadding;
private int mPointerMargin;
- private float mPointerWidth;
- private float mPointerHeight;
+ private int mPointerWidth;
+ private int mPointerHeight;
+ private int mPointerOverlap;
+ private int mManageButtonHeight;
+ private int mExpandedViewMinHeight;
+ private int mOverflowHeight;
private PointF mPinLocation;
private PointF mRestingStackPosition;
@@ -151,7 +157,6 @@ public class BubblePositioner {
Resources res = mContext.getResources();
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
- mBubbleBadgeSize = res.getDimensionPixelSize(R.dimen.bubble_badge_size);
mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -161,6 +166,10 @@ public class BubblePositioner {
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
+ mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap);
+ mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
+ mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
+ mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
mMaxBubbles = calculateMaxBubbles();
@@ -296,8 +305,8 @@ public class BubblePositioner {
return mPaddings;
}
- /** Calculates the y position of the expanded view when it is expanded. */
- public float getExpandedViewY() {
+ /** Gets the y position of the expanded view if it was top-aligned. */
+ private float getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
if (showBubblesVertically()) {
return top - mPointerWidth;
@@ -306,6 +315,116 @@ public class BubblePositioner {
}
}
+ /** The maximum height the expanded view can be. */
+ public int getMaxExpandedViewHeight(boolean isOverflow) {
+ int paddingTop = showBubblesVertically()
+ ? 0
+ : mPointerHeight;
+ int settingsHeight = isOverflow ? 0 : mManageButtonHeight;
+ // Subtract pointer size because it's laid out in LinearLayout with the expanded view.
+ int pointerSize = showBubblesVertically()
+ ? mPointerWidth
+ : (mPointerHeight + mPointerMargin);
+ // Subtract top insets because availableRect.height would account for that
+ int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+ return getAvailableRect().height()
+ - expandedContainerY
+ - paddingTop
+ - settingsHeight
+ - pointerSize;
+ }
+
+ /**
+ * Determines the height for the bubble, ensuring a minimum height. If the height should be as
+ * big as available, returns {@link #MAX_HEIGHT}.
+ */
+ public float getExpandedViewHeight(BubbleViewProvider bubble) {
+ boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+ float desiredHeight = isOverflow
+ ? mOverflowHeight
+ : ((Bubble) bubble).getDesiredHeight(mContext);
+ int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight;
+ desiredHeight = Math.max(manageButtonHeight + desiredHeight, mExpandedViewMinHeight);
+ if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) {
+ return MAX_HEIGHT;
+ }
+ return desiredHeight;
+ }
+
+ /**
+ * Gets the y position for the expanded view. This is the position on screen of the top
+ * horizontal line of the expanded view.
+ *
+ * @param bubble the bubble being positioned.
+ * @param bubblePosition the x position of the bubble if showing on top, the y position of the
+ * bubble if showing vertically.
+ * @return the y position for the expanded view.
+ */
+ public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) {
+ boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+ float expandedViewHeight = getExpandedViewHeight(bubble);
+ float topAlignment = getExpandedViewYTopAligned();
+ if (!showBubblesVertically() || expandedViewHeight == MAX_HEIGHT) {
+ // Top-align when bubbles are shown at the top or are max size.
+ return topAlignment;
+ }
+ // If we're here, we're showing vertically & developer has made height less than maximum.
+ int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight;
+ float pointerPosition = getPointerPosition(bubblePosition);
+ float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight;
+ float topIfCentered = pointerPosition - (expandedViewHeight / 2);
+ if (topIfCentered > mPositionRect.top && mPositionRect.bottom > bottomIfCentered) {
+ // Center it
+ return pointerPosition - mPointerWidth - (expandedViewHeight / 2f);
+ } else if (topIfCentered <= mPositionRect.top) {
+ // Top align
+ return topAlignment;
+ } else {
+ // Bottom align
+ return mPositionRect.bottom - manageButtonHeight - expandedViewHeight - mPointerWidth;
+ }
+ }
+
+ /**
+ * The position the pointer points to, the center of the bubble.
+ *
+ * @param bubblePosition the x position of the bubble if showing on top, the y position of the
+ * bubble if showing vertically.
+ * @return the position the tip of the pointer points to. The x position if showing on top, the
+ * y position if showing vertically.
+ */
+ public float getPointerPosition(float bubblePosition) {
+ // TODO: I don't understand why it works but it does - why normalized in portrait
+ // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
+ final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
+ getBubbleSize());
+ return showBubblesVertically()
+ ? bubblePosition + (getBubbleSize() / 2f)
+ : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
+ }
+
+ /**
+ * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
+ * row. When in landscape or on a large screen, they show at the left or right side in a
+ * vertical row. This method accounts for screen orientation and will return an x or y value
+ * for the position of the bubble in the row.
+ *
+ * @param index bubble index in the row.
+ * @param numberOfBubbles the number of bubbles (including the overflow) in the row.
+ * @return the y position of the bubble if showing vertically and the x position if showing
+ * horizontally.
+ */
+ public float getBubbleXOrYForOrientation(int index, int numberOfBubbles) {
+ final float positionInBar = index * (mBubbleSize + mSpacingBetweenBubbles);
+ final float expandedStackSize = (numberOfBubbles * mBubbleSize)
+ + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+ final float centerPosition = showBubblesVertically()
+ ? mPositionRect.centerY()
+ : mPositionRect.centerX();
+ final float rowStart = centerPosition - (expandedStackSize / 2f);
+ return rowStart + positionInBar;
+ }
+
/**
* Sets the stack's most recent position along the edge of the screen. This is saved when the
* last bubble is removed, so that the stack can be restored in its previous position.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 92e455ce4e3a..d46019510192 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -574,20 +574,17 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.setActiveController(mStackAnimationController);
hideFlyoutImmediate();
- if (!mPositioner.showingInTaskbar()) {
- // Also, save the magnetized stack so we can dispatch touch events to it.
- mMagnetizedObject = mStackAnimationController.getMagnetizedStack(
- mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mStackMagnetListener);
- } else {
+ if (mPositioner.showingInTaskbar()) {
// In taskbar, the stack isn't draggable so we shouldn't dispatch touch events.
mMagnetizedObject = null;
+ } else {
+ // Save the magnetized stack so we can dispatch touch events to it.
+ mMagnetizedObject = mStackAnimationController.getMagnetizedStack();
+ mMagnetizedObject.clearAllTargets();
+ mMagnetizedObject.addTarget(mMagneticTarget);
+ mMagnetizedObject.setMagnetListener(mStackMagnetListener);
}
- // Also, save the magnetized stack so we can dispatch touch events to it.
- mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mStackMagnetListener);
-
mIsDraggingStack = true;
// Cancel animations to make the stack temporarily invisible, since we're now
@@ -865,7 +862,6 @@ public class BubbleStackView extends FrameLayout
mRelativeStackPositionBeforeRotation = null;
}
- setUpDismissView();
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
beforeExpandedViewAnimation();
@@ -874,8 +870,10 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.expandFromStack(() -> {
afterExpandedViewAnimation();
} /* after */);
+ final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+ getBubbleIndex(mExpandedBubble));
mExpandedViewContainer.setTranslationX(0f);
- mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+ mExpandedViewContainer.setTranslationY(translationY);
mExpandedViewContainer.setAlpha(1f);
}
removeOnLayoutChangeListener(mOrientationChangedListener);
@@ -1027,10 +1025,9 @@ public class BubbleStackView extends FrameLayout
contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
- // MagnetizedObjects.
+ // MagnetizedObjects when the dismiss view gets shown.
mMagneticTarget = new MagnetizedObject.MagneticTarget(
mDismissView.getCircle(), dismissRadius);
-
mBubbleContainer.bringToFront();
}
@@ -1524,6 +1521,7 @@ public class BubbleStackView extends FrameLayout
bubble.cleanupViews();
}
updatePointerPosition();
+ updateExpandedView();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
}
@@ -1815,9 +1813,10 @@ public class BubbleStackView extends FrameLayout
mTaskbarScrim.setVisibility(VISIBLE);
mTaskbarScrim.animate().alpha(1f).start();
}
-
+ final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+ getBubbleIndex(mExpandedBubble));
mExpandedViewContainer.setTranslationX(0f);
- mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+ mExpandedViewContainer.setTranslationY(translationY);
mExpandedViewContainer.setAlpha(1f);
int index;
@@ -1866,7 +1865,7 @@ public class BubbleStackView extends FrameLayout
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
bubbleWillBeAt + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ translationY);
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -1970,7 +1969,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainerMatrix.setScale(
1f, 1f,
expandingFromBubbleAt + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ mPositioner.getExpandedViewY(mExpandedBubble, index));
}
mExpandedViewAlphaAnimator.reverse();
@@ -2077,7 +2076,7 @@ public class BubbleStackView extends FrameLayout
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
expandingFromBubbleDestination + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ mPositioner.getExpandedViewY(mExpandedBubble, expandingFromBubbleDestination));
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -2698,7 +2697,9 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
}
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+ mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
+ mExpandedAnimationController.getBubbleXOrYForOrientation(
+ getBubbleIndex(mExpandedBubble))));
mExpandedViewContainer.setTranslationX(0f);
mExpandedBubble.getExpandedView().updateView(
mExpandedViewContainer.getLocationOnScreen());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index df2b440c19df..efe07fbf108d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -83,12 +83,6 @@ public class ExpandedAnimationController
private float mBubblePaddingTop;
/** Size of each bubble. */
private float mBubbleSizePx;
- /** Max number of bubbles shown in row above expanded view. */
- private int mBubblesMaxRendered;
- /** Max amount of space to have between bubbles when expanded. */
- private int mBubblesMaxSpace;
- /** Amount of space between the bubbles when expanded. */
- private float mSpaceBetweenBubbles;
/** Whether the expand / collapse animation is running. */
private boolean mAnimatingExpand = false;
@@ -211,8 +205,6 @@ public class ExpandedAnimationController
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mBubbleSizePx = mPositioner.getBubbleSize();
- mBubblesMaxRendered = mPositioner.getMaxBubbles();
- mSpaceBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
}
/**
@@ -628,14 +620,13 @@ public class ExpandedAnimationController
}
}
- // TODO - could move to method on bubblePositioner if mSpaceBetweenBubbles gets moved
/**
* When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
* row. When in landscape or on a large screen, they show at the left or right side in a
* vertical row. This method accounts for screen orientation and will return an x or y value
* for the position of the bubble in the row.
*
- * @param index Bubble index in row.
+ * @param index bubble index in the row.
* @return the y position of the bubble if showing vertically and the x position if showing
* horizontally.
*/
@@ -643,15 +634,6 @@ public class ExpandedAnimationController
if (mLayout == null) {
return 0;
}
- final float positionInBar = index * (mBubbleSizePx + mSpaceBetweenBubbles);
- Rect availableRect = mPositioner.getAvailableRect();
- final boolean isLandscape = mPositioner.showBubblesVertically();
- final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx)
- + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
- final float centerPosition = isLandscape
- ? availableRect.centerY()
- : availableRect.centerX();
- final float rowStart = centerPosition - (expandedStackSize / 2f);
- return rowStart + positionInBar;
+ return mPositioner.getBubbleXOrYForOrientation(index, mLayout.getChildCount());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 0802fb59a008..636e1452aa9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -1024,11 +1024,9 @@ public class StackAnimationController extends
}
/**
- * Returns the {@link MagnetizedObject} instance for the bubble stack, with the provided
- * {@link MagnetizedObject.MagneticTarget} added as a target.
+ * Returns the {@link MagnetizedObject} instance for the bubble stack.
*/
- public MagnetizedObject<StackAnimationController> getMagnetizedStack(
- MagnetizedObject.MagneticTarget target) {
+ public MagnetizedObject<StackAnimationController> getMagnetizedStack() {
if (mMagnetizedStack == null) {
mMagnetizedStack = new MagnetizedObject<StackAnimationController>(
mLayout.getContext(),
@@ -1053,7 +1051,6 @@ public class StackAnimationController extends
loc[1] = (int) mStackPosition.y;
}
};
- mMagnetizedStack.addTarget(target);
mMagnetizedStack.setHapticsEnabled(true);
mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index a7996f056785..6f633691ba6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -279,9 +279,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (!mImeShowing) {
removeImeSurface();
}
- }
- if (mImeSourceControl != null) {
- mImeSourceControl.release(SurfaceControl::release);
+ if (mImeSourceControl != null) {
+ mImeSourceControl.release(SurfaceControl::release);
+ }
}
mImeSourceControl = imeSourceControl;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
index 9f6dd1f27b62..9e012598554b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
@@ -303,6 +303,13 @@ abstract class MagnetizedObject<T : Any>(
}
/**
+ * Removes all associated targets from this object.
+ */
+ fun clearAllTargets() {
+ associatedTargets.clear()
+ }
+
+ /**
* Provide this method with all motion events that move the magnetized object. If the
* location of the motion events moves within the magnetic field of a target, or indicate a
* fling-to-target gesture, this method will return true and you should not move the object
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 5b158d2063ba..8adfac01b462 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
@@ -92,12 +92,16 @@ public final class SplitLayout {
private DividerSnapAlgorithm mDividerSnapAlgorithm;
private int mDividePosition;
private boolean mInitialized = false;
+ private int mOrientation;
+ private int mRotation;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
mContext = context.createConfigurationContext(configuration);
+ mOrientation = configuration.orientation;
+ mRotation = configuration.windowConfiguration.getRotation();
mSplitLayoutHandler = splitLayoutHandler;
mDisplayImeController = displayImeController;
mSplitWindowManager = new SplitWindowManager(
@@ -144,25 +148,36 @@ public final class SplitLayout {
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
- final Rect rootBounds = configuration.windowConfiguration.getBounds();
- if (mRootBounds.equals(rootBounds)) {
- return false;
+ boolean affectsLayout = false;
+
+ // Make sure to render the divider bar with proper resources that matching the screen
+ // orientation.
+ final int orientation = configuration.orientation;
+ if (orientation != mOrientation) {
+ mOrientation = orientation;
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ affectsLayout = true;
}
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
- resetDividerPosition();
+ // 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.
+ final int rotation = configuration.windowConfiguration.getRotation();
+ final Rect rootBounds = configuration.windowConfiguration.getBounds();
+ if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+ mRootBounds.set(rootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ resetDividerPosition();
+ affectsLayout = true;
+ }
- // Don't inflate divider bar if it is not initialized.
- if (!mInitialized) {
- return false;
+ if (mInitialized) {
+ release();
+ init();
}
- release();
- init();
- return true;
+ return affectsLayout;
}
/** Updates recording bounds of divider window and both of the splits. */
@@ -271,7 +286,11 @@ public final class SplitLayout {
}
private void flingDividePosition(int from, int to) {
- if (from == to) return;
+ if (from == to) {
+ // No animation run, it should stop resizing here.
+ mSplitWindowManager.setResizingSplits(false);
+ return;
+ }
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
.setDuration(250);
@@ -296,9 +315,8 @@ public final class SplitLayout {
return context.getSystemService(WindowManager.class)
.getMaximumWindowMetrics()
.getWindowInsets()
- .getInsets(WindowInsets.Type.navigationBars()
- | WindowInsets.Type.statusBars()
- | WindowInsets.Type.displayCutout()).toRect();
+ .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())
+ .toRect();
}
private static boolean isLandscape(Rect bounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index 362b40f33e89..9bed40d67335 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -460,6 +460,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private void stopDragging() {
mHandle.setTouching(false, true /* animate */);
mWindowManager.setSlippery(true);
+ mWindowManagerProxy.setResizing(false);
releaseBackground();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index d9409ec2dc17..b1fa2ac25fe7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -204,7 +204,8 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition != mPendingDismiss && transition != mPendingEnter) {
// If we're not in split-mode, just abort
@@ -239,12 +240,12 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
if (change.getParent() != null) {
// This is probably reparented, so we want the parent to be immediately visible
final TransitionInfo.Change parentChange = info.getChange(change.getParent());
- t.show(parentChange.getLeash());
- t.setAlpha(parentChange.getLeash(), 1.f);
+ startTransaction.show(parentChange.getLeash());
+ startTransaction.setAlpha(parentChange.getLeash(), 1.f);
// and then animate this layer outside the parent (since, for example, this is
// the home task animating from fullscreen to part-screen).
- t.reparent(leash, info.getRootLeash());
- t.setLayer(leash, info.getChanges().size() - i);
+ startTransaction.reparent(leash, info.getRootLeash());
+ startTransaction.setLayer(leash, info.getChanges().size() - i);
// build the finish reparent/reposition
mFinishTransaction.reparent(leash, parentChange.getLeash());
mFinishTransaction.setPosition(leash,
@@ -271,12 +272,12 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
if (transition == mPendingEnter
&& mListener.mPrimary.token.equals(change.getContainer())
|| mListener.mSecondary.token.equals(change.getContainer())) {
- t.setWindowCrop(leash, change.getStartAbsBounds().width(),
+ startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(),
change.getStartAbsBounds().height());
if (mListener.mPrimary.token.equals(change.getContainer())) {
// Move layer to top since we want it above the oversized home task during
// animation even though home task is on top in hierarchy.
- t.setLayer(leash, info.getChanges().size() + 1);
+ startTransaction.setLayer(leash, info.getChanges().size() + 1);
}
}
boolean isOpening = Transitions.isOpeningType(info.getType());
@@ -289,7 +290,7 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
// Dismissing via snap-to-top/bottom means that the dismissed task is already
// not-visible (usually cropped to oblivion) so immediately set its alpha to 0
// and don't animate it so it doesn't pop-in when reparented.
- t.setAlpha(leash, 0.f);
+ startTransaction.setAlpha(leash, 0.f);
} else {
startExampleAnimation(leash, false /* show */);
}
@@ -311,7 +312,7 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
}
mSplitScreen.finishEnterSplitTransition(homeIsVisible);
}
- t.apply();
+ startTransaction.apply();
onFinish();
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f367cd608f37..6c3576b438b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -107,38 +107,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
- // Not a complete set of states but serves what we want right now.
- private enum State {
- UNDEFINED(0),
- TASK_APPEARED(1),
- ENTRY_SCHEDULED(2),
- ENTERING_PIP(3),
- ENTERED_PIP(4),
- EXITING_PIP(5);
-
- private final int mStateValue;
-
- State(int value) {
- mStateValue = value;
- }
-
- private boolean isInPip() {
- return mStateValue >= TASK_APPEARED.mStateValue
- && mStateValue != EXITING_PIP.mStateValue;
- }
-
- /**
- * Resize request can be initiated in other component, ignore if we are no longer in PIP,
- * still waiting for animation or we're exiting from it.
- *
- * @return {@code true} if the resize request should be blocked/ignored.
- */
- private boolean shouldBlockResizeRequest() {
- return mStateValue < ENTERING_PIP.mStateValue
- || mStateValue == EXITING_PIP.mStateValue;
- }
- }
-
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -190,7 +158,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
final boolean isExitPipDirection = isOutPipDirection(direction)
|| isRemovePipDirection(direction);
- if (mState != State.EXITING_PIP || isExitPipDirection) {
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
+ || isExitPipDirection) {
// Finish resize as long as we're not exiting PIP, or, if we are, only if this is
// the end of an exit PIP animation.
// This is necessary in case there was a resize animation ongoing when exit PIP
@@ -233,7 +202,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
- private State mState = State.UNDEFINED;
+ private PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -278,6 +247,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
+ @NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
@@ -291,6 +261,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@ShellMainThread ShellExecutor mainExecutor) {
mContext = context;
mSyncTransactionQueue = syncTransactionQueue;
+ mPipTransitionState = pipTransitionState;
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
@@ -326,14 +297,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
public boolean isInPip() {
- return mState.isInPip();
+ return mPipTransitionState.isInPip();
}
/**
* Returns whether the entry animation is waiting to be started.
*/
public boolean isEntryScheduled() {
- return mState == State.ENTRY_SCHEDULED;
+ return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED;
}
/**
@@ -401,9 +372,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* @param animationDurationMs duration in millisecond for the exiting PiP transition
*/
public void exitPip(int animationDurationMs) {
- if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) {
+ if (!mPipTransitionState.isInPip()
+ || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
+ || mToken == null) {
Log.wtf(TAG, "Not allowed to exitPip in current state"
- + " mState=" + mState + " mToken=" + mToken);
+ + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return;
}
@@ -427,7 +400,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setBoundsChangeTransaction(mToken, tx);
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
- mState = State.EXITING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mPipTransitionController.startTransition(destinationBounds, wct);
+ return;
+ }
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
// Make sure to grab the latest source hint rect as it could have been
@@ -465,9 +443,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* Removes PiP immediately.
*/
public void removePip() {
- if (!mState.isInPip() || mToken == null) {
+ if (!mPipTransitionState.isInPip() || mToken == null) {
Log.wtf(TAG, "Not allowed to removePip in current state"
- + " mState=" + mState + " mToken=" + mToken);
+ + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return;
}
@@ -481,10 +459,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
animator.setDuration(mExitAnimationDuration);
animator.setInterpolator(Interpolators.ALPHA_OUT);
animator.start();
- mState = State.EXITING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
}
private void removePipImmediately() {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mToken, null);
+ wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.reorder(mToken, false);
+ mPipTransitionController.startTransition(null, wct);
+ return;
+ }
+
try {
// Reset the task bounds first to ensure the activity configuration is reset as well
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -503,7 +490,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Objects.requireNonNull(info, "Requires RunningTaskInfo");
mTaskInfo = info;
mToken = mTaskInfo.token;
- mState = State.TASK_APPEARED;
+ mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED);
mLeash = leash;
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
@@ -546,6 +533,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
mPipMenuController.attach(mLeash);
+ } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
}
return;
}
@@ -557,7 +546,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
null /* updateBoundsCallback */);
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -584,7 +573,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
animateResizePip(currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */);
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}
/**
@@ -609,7 +598,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceControlTransactionFactory.getTransaction();
tx.setAlpha(mLeash, 0f);
tx.apply();
- mState = State.ENTRY_SCHEDULED;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
applyEnterPipSyncTransaction(destinationBounds, () -> {
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
@@ -620,11 +609,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
.start();
// mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition.
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}, null /* boundsChangeTransaction */);
}
private void onEndOfSwipePipToHomeTransition() {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mInSwipePipToHomeTransition = false;
+ mSwipePipToHomeOverlay = null;
+ return;
+ }
+
final Rect destinationBounds = mPipBoundsState.getBounds();
final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -667,7 +662,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}
mPipTransitionController.sendOnPipTransitionStarted(direction);
}
@@ -676,7 +671,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
void sendOnPipTransitionFinished(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mState = State.ENTERED_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
}
mPipTransitionController.sendOnPipTransitionFinished(direction);
// Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
@@ -701,7 +696,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
- if (mState == State.UNDEFINED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
return;
}
final WindowContainerToken token = info.token;
@@ -713,7 +708,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
clearWaitForFixedRotation();
mInSwipePipToHomeTransition = false;
mPictureInPictureParams = null;
- mState = State.UNDEFINED;
+ mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
// Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -735,8 +730,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
- if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) {
- Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState);
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP
+ && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) {
+ Log.d(TAG, "Defer onTaskInfoChange in current state: "
+ + mPipTransitionState.getTransitionState());
// Defer applying PiP parameters if the task is entering PiP to avoid disturbing
// the animation.
mDeferredTaskInfo = info;
@@ -769,7 +766,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mNextRotation = newRotation;
mWaitForFixedRotation = true;
- if (mState.isInPip()) {
+ if (mPipTransitionState.isInPip()) {
// Fade out the existing PiP to avoid jump cut during seamless rotation.
fadeExistingPip(false /* show */);
}
@@ -780,7 +777,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (!mWaitForFixedRotation) {
return;
}
- if (mState == State.TASK_APPEARED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
if (mInSwipePipToHomeTransition) {
onEndOfSwipePipToHomeTransition();
} else {
@@ -788,9 +785,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(),
mEnterAnimationDuration);
}
- } else if (mState == State.ENTERED_PIP && mHasFadeOut) {
+ } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP
+ && mHasFadeOut) {
fadeExistingPip(true /* show */);
- } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) {
+ } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP
+ && mDeferredAnimEndTransaction != null) {
final PipAnimationController.PipTransitionAnimator<?> animator =
mPipAnimationController.getCurrentAnimator();
final Rect destinationBounds = animator.getDestinationBounds();
@@ -844,13 +843,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// note that this can be called when swipe-to-home or fixed-rotation is happening.
// Skip this entirely if that's the case.
final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation
- && (mState != State.ENTERED_PIP);
+ && (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP);
if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) {
if (DEBUG) {
Log.d(TAG, "Skip onMovementBoundsChanged on rotation change"
+ " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition
+ " mWaitForFixedRotation=" + mWaitForFixedRotation
- + " mState=" + mState);
+ + " getTransitionState=" + mPipTransitionState.getTransitionState());
}
return;
}
@@ -858,7 +857,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
- final boolean rotatingPip = mState.isInPip() && fromRotation;
+ final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation;
if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
// The position will be used by fade-in animation when the fixed rotation is done.
mPipBoundsState.setBounds(destinationBoundsOut);
@@ -991,7 +990,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) {
- if (!mState.isInPip()) {
+ if (!mPipTransitionState.isInPip()) {
// TODO: tend to use shouldBlockResizeRequest here as well but need to consider
// the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
// container transaction callback and we want to set the mState immediately.
@@ -1021,7 +1020,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
- .round(tx, mLeash, mState.isInPip());
+ .round(tx, mLeash, mPipTransitionState.isInPip());
if (mPipMenuController.isMenuVisible()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
@@ -1099,7 +1098,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void scheduleFinishResizePip(Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
@@ -1116,7 +1115,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
.resetScale(tx, mLeash, destinationBounds)
- .round(tx, mLeash, mState.isInPip());
+ .round(tx, mLeash, mPipTransitionState.isInPip());
return tx;
}
@@ -1125,7 +1124,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
if (mWaitForFixedRotation) {
@@ -1383,7 +1382,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mToken=" + mToken
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
- pw.println(innerPrefix + "mState=" + mState);
+ pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
}
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 4759550c35c0..18153e3f4246 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
@@ -18,6 +18,8 @@ package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_PIP;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -25,9 +27,12 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.app.TaskInfo;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.Surface;
@@ -35,6 +40,7 @@ import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -49,64 +55,185 @@ import com.android.wm.shell.transition.Transitions;
*/
public class PipTransition extends PipTransitionController {
+ private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
+ private Rect mExitDestinationBounds = new Rect();
public PipTransition(Context context,
- PipBoundsState pipBoundsState, PipMenuController pipMenuController,
+ PipBoundsState pipBoundsState,
+ PipTransitionState pipTransitionState,
+ PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
+ mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
}
@Override
+ public void setIsFullAnimation(boolean isFullAnimation) {
+ setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA);
+ }
+
+ /**
+ * Sets the preferred animation type for one time.
+ * This is typically used to set the animation type to
+ * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
+ */
+ private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
+ mOneShotAnimationType = animationType;
+ }
+
+ @Override
+ public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ if (destinationBounds != null) {
+ mExitDestinationBounds.set(destinationBounds);
+ mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
+ } else {
+ mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
+ }
+ }
+
+ @Override
public boolean startAnimation(@android.annotation.NonNull IBinder transition,
@android.annotation.NonNull TransitionInfo info,
- @android.annotation.NonNull SurfaceControl.Transaction t,
+ @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
+ @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) {
+ final TransitionInfo.Change change = info.getChanges().get(0);
+ mFinishCallback = finishCallback;
+ startTransaction.apply();
+ boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
+ new Rect(mExitDestinationBounds));
+ mExitDestinationBounds.setEmpty();
+ return success;
+ }
+
+ if (info.getType() == TRANSIT_REMOVE_PIP) {
+ startTransaction.apply();
+ finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+ mPipBoundsState.getDisplayBounds());
+ finishCallback.onTransitionFinished(null, null);
+ return true;
+ }
+
+ // Search for an Enter PiP transition (along with a show wallpaper one)
+ TransitionInfo.Change enterPip = null;
+ TransitionInfo.Change wallpaper = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() != null
&& change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_PINNED) {
- mFinishCallback = finishCallback;
- return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t);
+ enterPip = change;
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ wallpaper = change;
}
}
- return false;
+ if (enterPip == null) {
+ return false;
+ }
+
+ // Show the wallpaper if there is a wallpaper change.
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash());
+ startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+ }
+
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+ mFinishCallback = finishCallback;
+ return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+ startTransaction, finishTransaction);
}
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- return null;
+ if (request.getType() == TRANSIT_PIP) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ wct.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED);
+ wct.setBounds(request.getTriggerTask().token, destinationBounds);
+ return wct;
+ } else {
+ return null;
+ }
}
@Override
public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
SurfaceControl.Transaction tx) {
+
+ if (isInPipDirection(direction)) {
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
prepareFinishResizeTransaction(taskInfo, destinationBounds,
direction, tx, wct);
- mFinishCallback.onTransitionFinished(wct, null);
+ mFinishCallback.onTransitionFinished(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) {
+ t.merge(tx);
+ t.apply();
+ }
+ });
finishResizeForMenu(destinationBounds);
}
+ private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+ final Rect destinationBounds) {
+ PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
+ mPipBoundsState.getBounds(), destinationBounds, null,
+ TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
+
+ animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start();
+
+ return true;
+ }
+
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
- final SurfaceControl.Transaction t) {
+ final SurfaceControl.Transaction startTransaction,
+ final SurfaceControl.Transaction finishTransaction) {
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
taskInfo.topActivityInfo);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
PipAnimationController.PipTransitionAnimator animator;
+ finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top);
+ if (taskInfo.pictureInPictureParams != null
+ && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+
+ // PiP menu is attached late in the process here to avoid any artifacts on the leash
+ // caused by addShellRoot when in gesture navigation mode.
+ mPipMenuController.attach(leash);
+ SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
+ .setPosition(leash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
+ startTransaction.merge(tx);
+ startTransaction.apply();
+ mPipBoundsState.setBounds(destinationBounds);
+ onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ mFinishCallback = null;
+ return true;
+ }
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
final Rect sourceHintRect =
PipBoundsAlgorithm.getValidSourceHintRect(
@@ -115,8 +242,10 @@ public class PipTransition extends PipTransitionController {
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
- t.setAlpha(leash, 0f);
- t.apply();
+ startTransaction.setAlpha(leash, 0f);
+ // PiP menu is attached late in the process here to avoid any artifacts on the leash
+ // caused by addShellRoot when in gesture navigation mode.
+ mPipMenuController.attach(leash);
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -124,6 +253,7 @@ public class PipTransition extends PipTransitionController {
throw new RuntimeException("Unrecognized animation type: "
+ mOneShotAnimationType);
}
+ startTransaction.apply();
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -158,6 +288,5 @@ public class PipTransition extends PipTransitionController {
}
wct.setBounds(taskInfo.token, taskBounds);
- wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index d801c918973a..dedc56678ef1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -29,6 +29,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.transition.Transitions;
@@ -46,6 +47,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
protected final PipBoundsState mPipBoundsState;
protected final ShellTaskOrganizer mShellTaskOrganizer;
protected final PipMenuController mPipMenuController;
+ protected final Transitions mTransitions;
private final Handler mMainHandler;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -98,6 +100,22 @@ public abstract class PipTransitionController implements Transitions.TransitionH
SurfaceControl.Transaction tx) {
}
+ /**
+ * Called to inform the transition that the animation should start with the assumption that
+ * PiP is not animating from its original bounds, but rather a continuation of another
+ * animation. For example, gesture navigation would first fade out the PiP activity, and the
+ * transition should be responsible to animate in (such as fade in) the PiP.
+ */
+ public void setIsFullAnimation(boolean isFullAnimation) {
+ }
+
+ /**
+ * Called when the Shell wants to starts a transition/animation.
+ */
+ public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ // Default implementation does nothing.
+ }
+
public PipTransitionController(PipBoundsState pipBoundsState,
PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController, Transitions transitions,
@@ -107,6 +125,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
mShellTaskOrganizer = shellTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipAnimationController = pipAnimationController;
+ mTransitions = transitions;
mMainHandler = new Handler(Looper.getMainLooper());
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.addHandler(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
new file mode 100644
index 000000000000..d23aada4133c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -0,0 +1,78 @@
+/*
+ * 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.wm.shell.pip;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and
+ * {@link PipTransition}.
+ */
+public class PipTransitionState {
+
+ public static final int UNDEFINED = 0;
+ public static final int TASK_APPEARED = 1;
+ public static final int ENTRY_SCHEDULED = 2;
+ public static final int ENTERING_PIP = 3;
+ public static final int ENTERED_PIP = 4;
+ public static final int EXITING_PIP = 5;
+
+ // Not a complete set of states but serves what we want right now.
+ @IntDef(prefix = { "TRANSITION_STATE_" }, value = {
+ UNDEFINED,
+ TASK_APPEARED,
+ ENTRY_SCHEDULED,
+ ENTERING_PIP,
+ ENTERED_PIP,
+ EXITING_PIP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransitionState {}
+
+ private @TransitionState int mState;
+
+ public PipTransitionState() {
+ mState = UNDEFINED;
+ }
+
+ public void setTransitionState(@TransitionState int state) {
+ mState = state;
+ }
+
+ public @TransitionState int getTransitionState() {
+ return mState;
+ }
+
+ public boolean isInPip() {
+ return mState >= TASK_APPEARED
+ && mState != EXITING_PIP;
+ }
+
+ /**
+ * Resize request can be initiated in other component, ignore if we are no longer in PIP,
+ * still waiting for animation or we're exiting from it.
+ *
+ * @return {@code true} if the resize request should be blocked/ignored.
+ */
+ public boolean shouldBlockResizeRequest() {
+ return mState < ENTERING_PIP
+ || mState == EXITING_PIP;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 4f3ec96968b2..62b50c55e77c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -67,6 +67,7 @@ import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
@@ -528,6 +529,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private void setPinnedStackAnimationType(int animationType) {
mPipTaskOrganizer.setOneShotAnimationType(animationType);
+ mPipTransitionController.setIsFullAnimation(
+ animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
}
private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index b7caf72641a3..551476dc9d54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -58,7 +58,8 @@ public class TvPipTransition extends PipTransitionController {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 8f0892fdcbba..df1f5e67e4e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -20,6 +20,8 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.window.IRemoteTransition;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
@@ -77,9 +79,22 @@ interface ISplitScreen {
int position, in Bundle options) = 9;
/**
- * Starts tasks simultaneously in one transition. The first task in the list will be in the
- * main-stage and on the left/top.
+ * Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
in Bundle sideOptions, int sidePosition, in IRemoteTransition remoteTransition) = 10;
+
+ /**
+ * Version of startTasks using legacy transition system.
+ */
+ oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
+ int sideTaskId, in Bundle sideOptions, int sidePosition,
+ in RemoteAnimationAdapter adapter) = 11;
+
+ /**
+ * Blocking call that notifies and gets additional split-screen targets when entering
+ * recents (for example: the dividerBar).
+ * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
+ */
+ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) = 12;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
new file mode 100644
index 000000000000..0b763f2d05f7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
@@ -0,0 +1,127 @@
+/*
+ * 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.wm.shell.splitscreen;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.view.WindowlessWindowManager;
+
+import com.android.wm.shell.R;
+
+/**
+ * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
+ * the consideration of display insets like status bar, navigation bar and display cutout.
+ */
+class OutlineManager extends WindowlessWindowManager {
+ private static final String WINDOW_NAME = "SplitOutlineLayer";
+ private final Context mContext;
+ private final Rect mOutlineBounds = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mLeash;
+ private int mOutlineColor;
+
+ OutlineManager(Context context, Configuration configuration) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ b.setParent(mHostLeash);
+ }
+
+ boolean drawOutlineBounds(Rect rootBounds) {
+ if (mLeash == null || mViewHost == null) return false;
+
+ computeOutlineBounds(mContext, rootBounds, mTmpBounds);
+ if (mOutlineBounds.equals(mTmpBounds)) {
+ return false;
+ }
+ mOutlineBounds.set(mTmpBounds);
+
+ ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor);
+ final WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ mViewHost.relayout(lp);
+
+ return true;
+ }
+
+ void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) {
+ if (mLeash != null || mViewHost != null) return;
+
+ mHostLeash = hostLeash;
+ mOutlineColor = color;
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext)
+ .inflate(R.layout.split_outline, null);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.token = new Binder();
+ lp.setTitle(WINDOW_NAME);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootView, lp);
+ mLeash = getSurfaceControl(mViewHost.getWindowToken());
+ t.setLayer(mLeash, Integer.MAX_VALUE);
+ }
+
+ void release() {
+ if (mViewHost != null) {
+ mViewHost.release();
+ }
+ }
+
+ private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {
+ computeDisplayStableBounds(context, outBounds);
+ outBounds.intersect(rootBounds);
+ // Offset the coordinate from screen based to surface based.
+ outBounds.offset(-rootBounds.left, -rootBounds.top);
+ }
+
+ private static void computeDisplayStableBounds(Context context, Rect outBounds) {
+ final WindowMetrics windowMetrics =
+ context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
+ outBounds.set(windowMetrics.getBounds());
+ outBounds.inset(windowMetrics.getWindowInsets().getInsets(
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
new file mode 100644
index 000000000000..71d48eeca71d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
@@ -0,0 +1,62 @@
+/*
+ * 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.wm.shell.splitscreen;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/** Root layout for holding split outline. */
+public class OutlineRoot extends FrameLayout {
+ public OutlineRoot(@NonNull Context context) {
+ super(context);
+ }
+
+ public OutlineRoot(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ private OutlineView mOutlineView;
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mOutlineView = findViewById(R.id.split_outline);
+ }
+
+ void updateOutlineBounds(Rect bounds, int color) {
+ mOutlineView.updateOutlineBounds(bounds, color);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
new file mode 100644
index 000000000000..ea66180e3dd2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
@@ -0,0 +1,76 @@
+/*
+ * 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.wm.shell.splitscreen;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.R;
+
+/** View for drawing split outline. */
+public class OutlineView extends View {
+ private final Paint mPaint = new Paint();
+ private final Rect mBounds = new Rect();
+
+ public OutlineView(@NonNull Context context) {
+ super(context);
+ }
+
+ public OutlineView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(getResources()
+ .getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
+ }
+
+ void updateOutlineBounds(Rect bounds, int color) {
+ if (mBounds.equals(bounds) && mPaint.getColor() == color) return;
+ mBounds.set(bounds);
+ mPaint.setColor(color);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mBounds.isEmpty()) return;
+ final Path path = new Region(mBounds).getBoundaryPath();
+ canvas.drawPath(path, mPaint);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 82f95a4f32ea..2b19bb965fed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,7 +16,10 @@
package com.android.wm.shell.splitscreen;
+import android.annotation.CallSuper;
import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -28,15 +31,19 @@ import com.android.wm.shell.common.SyncTransactionQueue;
/**
* Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
* here. All other task are launch in the {@link MainStage}.
+ *
* @see StageCoordinator
*/
class SideStage extends StageTaskListener {
private static final String TAG = SideStage.class.getSimpleName();
+ private final Context mContext;
+ private OutlineManager mOutlineManager;
- SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
+ SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
SurfaceSession surfaceSession) {
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+ mContext = context;
}
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
@@ -44,7 +51,7 @@ class SideStage extends StageTaskListener {
final WindowContainerToken rootToken = mRootTaskInfo.token;
wct.setBounds(rootToken, rootBounds)
.reparent(task.token, rootToken, true /* onTop*/)
- // Moving the root task to top after the child tasks were repareted , or the root
+ // Moving the root task to top after the child tasks were reparented , or the root
// task cannot be visible and focused.
.reorder(rootToken, true /* onTop */);
}
@@ -69,4 +76,34 @@ class SideStage extends StageTaskListener {
wct.reparent(task.token, newParent, false /* onTop */);
return true;
}
+
+ void enableOutline(boolean enable) {
+ if (enable) {
+ if (mOutlineManager == null && mRootTaskInfo != null) {
+ mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration);
+ mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW));
+ updateOutlineBounds();
+ }
+ } else {
+ if (mOutlineManager != null) {
+ mOutlineManager.release();
+ mOutlineManager = null;
+ }
+ }
+ }
+
+ private void updateOutlineBounds() {
+ if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return;
+ mOutlineManager.drawOutlineBounds(
+ mRootTaskInfo.configuration.windowConfiguration.getBounds());
+ }
+
+ @Override
+ @CallSuper
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ super.onTaskInfoChanged(taskInfo);
+ if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) {
+ updateOutlineBounds();
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 002bfb6e429f..d6afeba9fed9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,6 +64,12 @@ public interface SplitScreen {
return null;
}
+ /**
+ * Called when the keyguard occluded state changes.
+ * @param occluded Indicates if the keyguard is now occluded.
+ */
+ void onKeyguardOccludedChanged(boolean occluded);
+
/** Get a string representation of a stage type */
static String stageTypeToString(@StageType int stage) {
switch (stage) {
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 9a457b5fd88e..d60fa29d8f77 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
@@ -39,6 +39,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.window.IRemoteTransition;
import androidx.annotation.BinderThread;
@@ -140,6 +142,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.removeFromSideStage(taskId);
}
+ public void setSideStageOutline(boolean enable) {
+ mStageCoordinator.setSideStageOutline(enable);
+ }
+
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
@@ -157,6 +163,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.exitSplitScreen();
}
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mStageCoordinator.onKeyguardOccludedChanged(occluded);
+ }
+
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
}
@@ -262,6 +272,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return options;
}
+ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+ if (!isSplitScreenVisible()) return null;
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
pw.println(prefix + TAG);
if (mStageCoordinator != null) {
@@ -284,6 +299,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
return mISplitScreen;
}
+
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onKeyguardOccludedChanged(occluded);
+ });
+ }
}
/**
@@ -417,6 +439,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
+ public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+ int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ RemoteAnimationAdapter adapter) {
+ executeRemoteCallWithTaskPermission(mController, "startTasks",
+ (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
+ mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
+ adapter));
+ }
+
+ @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition,
@@ -444,5 +476,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
controller.startIntent(intent, fillInIntent, stage, position, options);
});
}
+
+ @Override
+ public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+ final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
+ executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
+ (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel),
+ true /* blocking */);
+ return out[0];
+ }
}
}
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 c37789ecbc9d..69d0be6abc0b 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
@@ -84,17 +84,19 @@ class SplitScreenTransitions {
}
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
if (mRemoteHandler != null) {
- mRemoteHandler.startAnimation(transition, info, t, mRemoteFinishCB);
+ mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
+ mRemoteFinishCB);
mRemoteHandler = null;
return;
}
- playInternalAnimation(transition, info, t, mainRoot, sideRoot);
+ playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
}
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
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 0264c5a1c55a..a89ce9ae76f2 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
@@ -20,6 +20,7 @@ import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -42,11 +43,21 @@ import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -115,7 +126,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
private final DisplayImeController mDisplayImeController;
private final SplitScreenTransitions mSplitTransitions;
- private boolean mExitSplitScreenOnHide = true;
+ private boolean mExitSplitScreenOnHide;
+ private boolean mKeyguardOccluded;
// TODO(b/187041611): remove this flag after totally deprecated legacy split
/** Whether the device is supporting legacy split or not. */
@@ -150,6 +162,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue,
mSurfaceSession);
mSideStage = new SideStage(
+ mContext,
mTaskOrganizer,
mDisplayId,
mSideStageListener,
@@ -157,6 +170,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSurfaceSession);
mDisplayImeController = displayImeController;
mRootTDAOrganizer.registerListener(displayId, this);
+ final DeviceStateManager deviceStateManager =
+ mContext.getSystemService(DeviceStateManager.class);
+ deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
+ new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
transitions.addHandler(this);
@@ -215,6 +232,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return result;
}
+ void setSideStageOutline(boolean enable) {
+ mSideStage.enableOutline(enable);
+ }
+
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
@@ -241,6 +262,75 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
}
+ /** Starts 2 tasks in one legacy transition. */
+ void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+ int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ RemoteAnimationAdapter adapter) {
+ 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
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ RemoteAnimationTarget[] augmentedNonApps =
+ new RemoteAnimationTarget[nonApps.length + 1];
+ for (int i = 0; i < nonApps.length; ++i) {
+ augmentedNonApps[i] = nonApps[i];
+ }
+ augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ adapter.getCallingApplication());
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
+ finishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ try {
+ adapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+ };
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+ wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
+
+ if (mainOptions == null) {
+ mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+ } else {
+ ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+ mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+ }
+
+ sideOptions = sideOptions != null ? sideOptions : new Bundle();
+ setSideStagePosition(sidePosition);
+
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(getMainStageBounds(), wct);
+ mSideStage.setBounds(getSideStageBounds(), wct);
+
+ // Make sure the launch options will put tasks in the corresponding split roots
+ addActivityOptions(mainOptions, mMainStage);
+ addActivityOptions(sideOptions, mSideStage);
+
+ // Add task launch requests
+ wct.startTask(mainTaskId, mainOptions);
+ wct.startTask(sideTaskId, sideOptions);
+
+ // Using legacy transitions, so we can't use blast sync since it conflicts.
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
@SplitLayout.SplitPosition
int getSideStagePosition() {
return mSideStagePosition;
@@ -253,17 +343,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
void setSideStagePosition(@SplitPosition int sideStagePosition) {
- setSideStagePosition(sideStagePosition, true /* updateVisibility */);
+ setSideStagePosition(sideStagePosition, true /* updateBounds */);
}
private void setSideStagePosition(@SplitPosition int sideStagePosition,
- boolean updateVisibility) {
+ boolean updateBounds) {
if (mSideStagePosition == sideStagePosition) return;
mSideStagePosition = sideStagePosition;
sendOnStagePositionChanged();
- if (mSideStageListener.mVisible && updateVisibility) {
- onStageVisibilityChanged(mSideStageListener);
+ if (mSideStageListener.mVisible && updateBounds) {
+ onBoundsChanged(mSplitLayout);
}
}
@@ -275,6 +365,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
+ void onKeyguardOccludedChanged(boolean occluded) {
+ // Do not exit split directly, because it needs to wait for task info update to determine
+ // which task should remain on top after split dismissed.
+ mKeyguardOccluded = occluded;
+ }
+
void exitSplitScreen() {
exitSplitScreen(null /* childrenToTop */);
}
@@ -403,22 +499,37 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Divider is only visible if both the main stage and side stages are visible
setDividerVisibility(isSplitScreenVisible());
- if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
- // Exit split-screen if both stage are not visible.
- // TODO: This is only a temporary request from UX and is likely to be removed soon...
- exitSplitScreen();
+ if (!mainStageVisible && !sideStageVisible) {
+ if (mExitSplitScreenOnHide
+ // Don't dismiss staged split when both stages are not visible due to sleeping display,
+ // like the cases keyguard showing or screen off.
+ || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
+ exitSplitScreen();
+ }
+ } else if (mKeyguardOccluded) {
+ // At least one of the stages is visible while keyguard occluded. Dismiss split because
+ // there's show-when-locked activity showing on top of keyguard. Also make sure the
+ // task contains show-when-locked activity remains on top after split dismissed.
+ final StageTaskListener toTop =
+ mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
+ exitSplitScreen(toTop);
}
- if (mainStageVisible) {
+ // When both stage's visibility changed to visible, main stage might receives visibility
+ // changed before side stage if it has higher z-order than side stage. Make sure we only
+ // update main stage's windowing mode with the visibility changed of side stage to prevent
+ // stacking multiple windowing mode transactions which result to flicker issue.
+ if (mainStageVisible && stageListener == mSideStageListener) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideStageVisible) {
// The main stage configuration should to follow split layout when side stage is
// visible.
mMainStage.updateConfiguration(
WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
- } else {
+ } else if (!mSideStage.mRootTaskInfo.isSleeping) {
// We want the main stage configuration to be fullscreen when the side stage isn't
// visible.
+ // We should not do it when side stage are not visible due to sleeping display too.
mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
}
// TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
@@ -487,10 +598,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.setBounds(getSideStageBounds(), wct);
- // Reorder side stage to the top whenever there's a new child task appeared in side
- // stage. This is needed to prevent main stage occludes side stage and makes main stage
- // flipping between fullscreen and multi-window windowing mode.
- wct.reorder(mSideStage.mRootTaskInfo.token, true);
mTaskOrganizer.applyTransaction(wct);
}
}
@@ -580,11 +687,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
mDisplayAreaInfo = displayAreaInfo;
if (mSplitLayout != null
- && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)) {
+ && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
+ && mMainStage.isActive()) {
onBoundsChanged(mSplitLayout);
}
}
+ private void onFoldedStateChanged(boolean folded) {
+ if (folded && mMainStage.isActive()) {
+ exitSplitScreen(mMainStage);
+ }
+ }
+
private Rect getSideStageBounds() {
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
@@ -672,7 +786,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public boolean startAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition != mSplitTransitions.mPendingDismiss
&& transition != mSplitTransitions.mPendingEnter) {
@@ -717,14 +832,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
- shouldAnimate = startPendingEnterAnimation(transition, info, t);
+ shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
} else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, t);
+ shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
}
if (!shouldAnimate) return false;
- mSplitTransitions.playAnimation(transition, info, t, finishCallback,
- mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
+ finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
return true;
}
@@ -754,7 +869,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Update local states (before animating).
setDividerVisibility(true);
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
+ setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */);
setSplitsVisible(true);
addDividerBarToTransition(info, t, true /* show */);
@@ -860,6 +975,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ RemoteAnimationTarget getDividerBarLegacyTarget() {
+ final Rect bounds = mSplitLayout.getDividerBounds();
+ return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
+ mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */,
+ null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
+ new android.graphics.Point(0, 0) /* position */, bounds, bounds,
+ new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
+ null /* taskInfo */, TYPE_DOCK_DIVIDER);
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 0fd8eca6290e..4f73feec750d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -71,8 +71,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
private final StageListenerCallbacks mCallbacks;
- private final SyncTransactionQueue mSyncQueue;
private final SurfaceSession mSurfaceSession;
+ protected final SyncTransactionQueue mSyncQueue;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
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 c6fb5af7d4be..7f42fe9dc406 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
@@ -16,6 +16,13 @@
package com.android.wm.shell.transition;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
@@ -24,22 +31,34 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.IBinder;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -61,30 +80,55 @@ import java.util.ArrayList;
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static final int MAX_ANIMATION_DURATION = 3000;
+ /**
+ * Restrict ability of activities overriding transition animation in a way such that
+ * an activity can do it only when the transition happens within a same task.
+ *
+ * @see android.app.Activity#overridePendingTransition(int, int)
+ */
+ private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
+ "persist.wm.disable_custom_task_animation";
+
+ /**
+ * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
+ */
+ static boolean sDisableCustomTaskAnimationProperty =
+ SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+
private final TransactionPool mTransactionPool;
+ private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
+
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
private final Rect mInsets = new Rect(0, 0, 0, 0);
private float mTransitionAnimationScaleSetting = 1.0f;
+ private final int mCurrentUserId;
+
+ private ScreenRotationAnimation mRotationAnimation;
+
DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mTransactionPool = transactionPool;
+ mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
+ mCurrentUserId = UserHandle.myUserId();
AttributeCache.init(context);
}
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"start default transition animation, info = %s", info);
@@ -97,19 +141,38 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
+
+ if (mRotationAnimation != null) {
+ mRotationAnimation.kill();
+ mRotationAnimation = null;
+ }
+
mAnimations.remove(transition);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+
+ final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
+
+ if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
+ && (change.getEndRotation() != change.getStartRotation())
+ && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+ mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash());
+ mRotationAnimation.startAnimation(animations, onAnimFinish,
+ mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ continue;
+ }
+
if (change.getMode() == TRANSIT_CHANGE) {
// No default animation for this, so just update bounds/position.
- t.setPosition(change.getLeash(),
+ startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - change.getEndRelOffset().x,
change.getEndAbsBounds().top - change.getEndRelOffset().y);
if (change.getTaskInfo() != null) {
// Skip non-tasks since those usually have null bounds.
- t.setWindowCrop(change.getLeash(),
+ startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
}
@@ -117,12 +180,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
- Animation a = loadAnimation(info.getType(), info.getFlags(), change);
+ Animation a = loadAnimation(info, change, wallpaperTransit);
if (a != null) {
- startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */);
+
+ if (info.getAnimationOptions() != null) {
+ attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+ }
}
}
- t.apply();
+ startTransaction.apply();
// run finish now in-case there are no animations
onAnimFinish.run();
return true;
@@ -141,87 +209,134 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
@Nullable
- private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) {
- // TODO(b/178678389): It should handle more type animation here
+ private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change,
+ int wallpaperTransit) {
Animation a = null;
- final boolean isOpening = Transitions.isOpeningType(type);
+ final int type = info.getType();
+ final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
+ final boolean isOpeningType = Transitions.isOpeningType(type);
+ final boolean enter = Transitions.isOpeningType(changeMode);
+ final boolean isTask = change.getTaskInfo() != null;
+ final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+ final int overrideType = options != null ? options.getType() : ANIM_NONE;
+ final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
- if (type == TRANSIT_RELAUNCH) {
- a = mTransitionAnimation.createRelaunchAnimation(
- change.getStartAbsBounds(), mInsets, change.getEndAbsBounds());
- } else if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
+ if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
(changeFlags & FLAG_SHOW_WALLPAPER) != 0);
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
- } else if (changeMode == TRANSIT_OPEN && isOpening) {
- if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- return null;
- }
-
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
- } else if (change.getTaskInfo() != null) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskOpenEnterAnimation);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
- }
- } else if (changeMode == TRANSIT_TO_FRONT && isOpening) {
- if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- return null;
- }
-
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToFrontEnterAnimation);
- }
- } else if (changeMode == TRANSIT_CLOSE && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
- } else if (change.getTaskInfo() != null) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskCloseExitAnimation);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
- }
- } else if (changeMode == TRANSIT_TO_BACK && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
+ } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
+ if (isOpeningType) {
+ a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
} else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToBackExitAnimation);
+ a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
}
} else if (changeMode == TRANSIT_CHANGE) {
// In the absence of a specific adapter, we just want to keep everything stationary.
a = new AlphaAnimation(1.f, 1.f);
a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+ } else if (type == TRANSIT_RELAUNCH) {
+ a = mTransitionAnimation.createRelaunchAnimation(
+ change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
+ } else if (overrideType == ANIM_CUSTOM
+ && (canCustomContainer || options.getOverrideTaskTransition())) {
+ a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
+ ? options.getEnterResId() : options.getExitResId());
+ } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) {
+ a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
+ } else if (overrideType == ANIM_CLIP_REVEAL) {
+ a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
+ change.getEndAbsBounds(), change.getEndAbsBounds(),
+ options.getTransitionBounds());
+ } else if (overrideType == ANIM_SCALE_UP) {
+ a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
+ change.getEndAbsBounds(), options.getTransitionBounds());
+ } else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
+ || overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
+ final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
+ change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
+ options.getTransitionBounds());
+ } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
+ } else if (type == TRANSIT_OPEN) {
+ if (isTask) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskOpenEnterAnimation
+ : R.styleable.WindowAnimation_taskOpenExitAnimation);
+ } else {
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+ a = mTransitionAnimation.loadDefaultAnimationRes(
+ R.anim.activity_translucent_open_enter);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityOpenEnterAnimation
+ : R.styleable.WindowAnimation_activityOpenExitAnimation);
+ }
+ }
+ } else if (type == TRANSIT_TO_FRONT) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
+ : R.styleable.WindowAnimation_taskToFrontExitAnimation);
+ } else if (type == TRANSIT_CLOSE) {
+ if (isTask) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskCloseEnterAnimation
+ : R.styleable.WindowAnimation_taskCloseExitAnimation);
+ } else {
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+ a = mTransitionAnimation.loadDefaultAnimationRes(
+ R.anim.activity_translucent_close_exit);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : R.styleable.WindowAnimation_activityCloseExitAnimation);
+ }
+ }
+ } else if (type == TRANSIT_TO_BACK) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskToBackEnterAnimation
+ : R.styleable.WindowAnimation_taskToBackExitAnimation);
}
if (a != null) {
- Rect start = change.getStartAbsBounds();
- Rect end = change.getEndAbsBounds();
+ if (!a.isInitialized()) {
+ Rect end = change.getEndAbsBounds();
+ a.initialize(end.width(), end.height(), end.width(), end.height());
+ }
a.restrictDuration(MAX_ANIMATION_DURATION);
- a.initialize(end.width(), end.height(), start.width(), start.height());
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
}
return a;
}
- private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
- @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ static void startSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Animation anim, @NonNull SurfaceControl leash,
+ @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
+ @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
+ @Nullable Point position) {
+ final SurfaceControl.Transaction transaction = pool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
final float[] matrix = new float[9];
@@ -231,14 +346,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
va.addUpdateListener(animation -> {
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
- applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
+ applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
+ position);
});
final Runnable finisher = () -> {
- applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
+ applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
+ position);
- mTransactionPool.release(transaction);
- mMainExecutor.execute(() -> {
+ pool.release(transaction);
+ mainExecutor.execute(() -> {
animations.remove(va);
finishCallback.run();
});
@@ -255,12 +372,116 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
});
animations.add(va);
- mAnimExecutor.execute(va::start);
+ animExecutor.execute(va::start);
+ }
+
+ private void attachThumbnail(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change,
+ TransitionInfo.AnimationOptions options) {
+ final boolean isTask = change.getTaskInfo() != null;
+ final boolean isOpen = Transitions.isOpeningType(change.getMode());
+ final boolean isClose = Transitions.isClosingType(change.getMode());
+ if (isOpen) {
+ if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
+ attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+ } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
+ attachThumbnailAnimation(animations, finishCallback, change, options);
+ }
+ } else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
+ attachThumbnailAnimation(animations, finishCallback, change, options);
+ }
+ }
+
+ private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change) {
+ final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
+ ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
+ final Rect bounds = change.getEndAbsBounds();
+ final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
+ thumbnailDrawableRes, bounds);
+ if (thumbnail == null) {
+ return;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ change.getLeash(), thumbnail, transaction);
+ final Animation a =
+ mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds);
+ if (a == null) {
+ return;
+ }
+
+ final Runnable finisher = () -> {
+ wt.destroy(transaction);
+ mTransactionPool.release(transaction);
+
+ finishCallback.run();
+ };
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+ mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top));
+ }
+
+ private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change,
+ TransitionInfo.AnimationOptions options) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ change.getLeash(), options.getThumbnail(), transaction);
+ final Rect bounds = change.getEndAbsBounds();
+ final int orientation = mContext.getResources().getConfiguration().orientation;
+ final Animation a = mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(bounds,
+ mInsets, options.getThumbnail(), orientation, null /* startRect */,
+ options.getTransitionBounds(), options.getType() == ANIM_THUMBNAIL_SCALE_UP);
+
+ final Runnable finisher = () -> {
+ wt.destroy(transaction);
+ mTransactionPool.release(transaction);
+
+ finishCallback.run();
+ };
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+ mMainExecutor, mAnimExecutor, null /* position */);
+ }
+
+ private static int getWallpaperTransitType(TransitionInfo info) {
+ boolean hasOpenWallpaper = false;
+ boolean hasCloseWallpaper = false;
+
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
+ if (Transitions.isOpeningType(change.getMode())) {
+ hasOpenWallpaper = true;
+ } else if (Transitions.isClosingType(change.getMode())) {
+ hasCloseWallpaper = true;
+ }
+ }
+ }
+
+ if (hasOpenWallpaper && hasCloseWallpaper) {
+ return Transitions.isOpeningType(info.getType())
+ ? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE;
+ } else if (hasOpenWallpaper) {
+ return WALLPAPER_TRANSITION_OPEN;
+ } else if (hasCloseWallpaper) {
+ return WALLPAPER_TRANSITION_CLOSE;
+ } else {
+ return WALLPAPER_TRANSITION_NONE;
+ }
}
private static void applyTransformation(long time, SurfaceControl.Transaction t,
- SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
+ SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
+ Point position) {
anim.getTransformation(time, transformation);
+ if (position != null) {
+ transformation.getMatrix().postTranslate(position.x, position.y);
+ }
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4da6664aa3dc..6bd805323aa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -57,7 +57,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mTransition != transition) return false;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
@@ -70,19 +71,24 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
}
};
try {
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.startAnimation(transition, info, t, cb);
+ mRemote.startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (mRemote.asBinder() != null) {
@@ -102,7 +108,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
mMainExecutor.execute(
() -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 9bfb261fcb85..f432049f44f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -56,14 +56,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
new ArrayList<>();
- private final IBinder.DeathRecipient mTransitionDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- @BinderThread
- public void binderDied() {
- mMainExecutor.execute(() -> mFilters.clear());
- }
- };
+ private final ArrayMap<IBinder, RemoteDeathHandler> mDeathHandlers = new ArrayMap<>();
RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
@@ -71,7 +64,9 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
void addFiltered(TransitionFilter filter, IRemoteTransition remote) {
try {
- remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+ RemoteDeathHandler handler = new RemoteDeathHandler(remote.asBinder());
+ remote.asBinder().linkToDeath(handler, 0 /* flags */);
+ mDeathHandlers.put(remote.asBinder(), handler);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
@@ -88,7 +83,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
}
if (removed) {
- remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+ RemoteDeathHandler handler = mDeathHandlers.remove(remote.asBinder());
+ remote.asBinder().unlinkToDeath(handler, 0 /* flags */);
}
}
@@ -99,7 +95,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
if (pendingRemote == null) {
@@ -132,11 +129,15 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
mMainExecutor.execute(() -> {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mRequestedRemotes.remove(transition);
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
@@ -146,7 +147,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
if (remote.asBinder() != null) {
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- remote.startAnimation(transition, info, t, cb);
+ remote.startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (remote.asBinder() != null) {
@@ -170,7 +171,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
mMainExecutor.execute(() -> {
if (!mRequestedRemotes.containsKey(mergeTarget)) {
Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
@@ -200,4 +202,25 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
+ " for %s: %s", transition, remote);
return new WindowContainerTransaction();
}
+
+ /** NOTE: binder deaths can alter the filter order */
+ private class RemoteDeathHandler implements IBinder.DeathRecipient {
+ private final IBinder mRemote;
+
+ RemoteDeathHandler(IBinder remote) {
+ mRemote = remote;
+ }
+
+ @Override
+ @BinderThread
+ public void binderDied() {
+ mMainExecutor.execute(() -> {
+ for (int i = mFilters.size() - 1; i >= 0; --i) {
+ if (mRemote.equals(mFilters.get(i).second.asBinder())) {
+ mFilters.remove(i);
+ }
+ }
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
new file mode 100644
index 000000000000..a13b03d91cdf
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -0,0 +1,497 @@
+/*
+ * 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.wm.shell.transition;
+
+import static android.hardware.HardwareBuffer.RGBA_8888;
+import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
+import static android.util.RotationUtils.deltaRotation;
+
+import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
+import static com.android.wm.shell.transition.Transitions.TAG;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceSession;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.window.TransitionInfo;
+
+import com.android.internal.R;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class handles the rotation animation when the device is rotated.
+ *
+ * <p>
+ * The screen rotation animation is composed of 4 different part:
+ * <ul>
+ * <li> The screenshot: <p>
+ * A screenshot of the whole screen prior the change of orientation is taken to hide the
+ * element resizing below. The screenshot is then animated to rotate and cross-fade to
+ * the new orientation with the content in the new orientation.
+ *
+ * <li> The windows on the display: <p>y
+ * Once the device is rotated, the screen and its content are in the new orientation. The
+ * animation first rotate the new content into the old orientation to then be able to
+ * animate to the new orientation
+ *
+ * <li> The Background color frame: <p>
+ * To have the animation seem more seamless, we add a color transitioning background behind the
+ * exiting and entering layouts. We compute the brightness of the start and end
+ * layouts and transition from the two brightness values as grayscale underneath the animation
+ *
+ * <li> The entering Blackframe: <p>
+ * The enter Blackframe is similar to the exit Blackframe but is only used when a custom
+ * rotation animation is used and matches the new content size instead of the screenshot.
+ * </ul>
+ */
+class ScreenRotationAnimation {
+
+ /** How much to multiply the policy's type layer, to reserve room
+ * for multiple windows of the same type and Z-ordering adjustment
+ * with TYPE_LAYER_OFFSET. */
+ static final int TYPE_LAYER_MULTIPLIER = 10000;
+ static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+
+ /*
+ * Layers for screen rotation animation. We put these layers above
+ * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+ */
+ private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
+
+ static final int MAX_ANIMATION_DURATION = 10 * 1000;
+
+ private final Context mContext;
+ private final TransactionPool mTransactionPool;
+ private final float[] mTmpFloats = new float[9];
+ // Complete transformations being applied.
+ private final Matrix mSnapshotInitialMatrix = new Matrix();
+ /** The leash of display. */
+ private final SurfaceControl mSurfaceControl;
+ private final Rect mStartBounds = new Rect();
+ private final Rect mEndBounds = new Rect();
+
+ private final int mStartWidth;
+ private final int mStartHeight;
+ private final int mEndWidth;
+ private final int mEndHeight;
+ private final int mStartRotation;
+ private final int mEndRotation;
+
+ /** This layer contains the actual screenshot that is to be faded out. */
+ private SurfaceControl mScreenshotLayer;
+ /**
+ * Only used for screen rotation and not custom animations. Layered behind all other layers
+ * to avoid showing any "empty" spots
+ */
+ private SurfaceControl mBackColorSurface;
+ /** The leash using to animate screenshot layer. */
+ private SurfaceControl mAnimLeash;
+ private Transaction mTransaction;
+
+ // The current active animation to move from the old to the new rotated
+ // state. Which animation is run here will depend on the old and new
+ // rotations.
+ private Animation mRotateExitAnimation;
+ private Animation mRotateEnterAnimation;
+
+ /** Intensity of light/whiteness of the layout before rotation occurs. */
+ private float mStartLuma;
+ /** Intensity of light/whiteness of the layout after rotation occurs. */
+ private float mEndLuma;
+
+ ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
+ Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash) {
+ mContext = context;
+ mTransactionPool = pool;
+
+ mSurfaceControl = change.getLeash();
+ mStartWidth = change.getStartAbsBounds().width();
+ mStartHeight = change.getStartAbsBounds().height();
+ mEndWidth = change.getEndAbsBounds().width();
+ mEndHeight = change.getEndAbsBounds().height();
+ mStartRotation = change.getStartRotation();
+ mEndRotation = change.getEndRotation();
+
+ mStartBounds.set(change.getStartAbsBounds());
+ mEndBounds.set(change.getEndAbsBounds());
+
+ mAnimLeash = new SurfaceControl.Builder(session)
+ .setParent(rootLeash)
+ .setEffectLayer()
+ .setCallsite("ShellRotationAnimation")
+ .setName("Animation leash of screenshot rotation")
+ .build();
+
+ try {
+ SurfaceControl.LayerCaptureArgs args =
+ new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
+ .build();
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(args);
+ if (screenshotBuffer == null) {
+ Slog.w(TAG, "Unable to take screenshot of display");
+ return;
+ }
+
+ mBackColorSurface = new SurfaceControl.Builder(session)
+ .setParent(rootLeash)
+ .setColorLayer()
+ .setCallsite("ShellRotationAnimation")
+ .setName("BackColorSurface")
+ .build();
+
+ mScreenshotLayer = new SurfaceControl.Builder(session)
+ .setParent(mAnimLeash)
+ .setBLASTLayer()
+ .setSecure(screenshotBuffer.containsSecureLayers())
+ .setCallsite("ShellRotationAnimation")
+ .setName("RotationLayer")
+ .build();
+
+ HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
+
+ GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
+ screenshotBuffer.getHardwareBuffer());
+
+ t.setLayer(mBackColorSurface, -1);
+ t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+ t.setAlpha(mBackColorSurface, 1);
+ t.show(mBackColorSurface);
+
+ t.setPosition(mAnimLeash, 0, 0);
+ t.setAlpha(mAnimLeash, 1);
+ t.show(mAnimLeash);
+
+ t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
+ t.setBuffer(mScreenshotLayer, buffer);
+ t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
+ t.show(mScreenshotLayer);
+
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate freeze surface", e);
+ }
+
+ setRotation(t);
+ t.apply();
+ }
+
+ private void setRotation(SurfaceControl.Transaction t) {
+ // Compute the transformation matrix that must be applied
+ // to the snapshot to make it stay in the same original position
+ // with the current screen rotation.
+ int delta = deltaRotation(mEndRotation, mStartRotation);
+ createRotationMatrix(delta, mStartWidth, mStartHeight, mSnapshotInitialMatrix);
+ setRotationTransform(t, mSnapshotInitialMatrix);
+ }
+
+ private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
+ if (mScreenshotLayer == null) {
+ return;
+ }
+ matrix.getValues(mTmpFloats);
+ float x = mTmpFloats[Matrix.MTRANS_X];
+ float y = mTmpFloats[Matrix.MTRANS_Y];
+ t.setPosition(mScreenshotLayer, x, y);
+ t.setMatrix(mScreenshotLayer,
+ mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+ mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+
+ t.setAlpha(mScreenshotLayer, (float) 1.0);
+ t.show(mScreenshotLayer);
+ }
+
+ /**
+ * Returns true if animating.
+ */
+ public boolean startAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, float animationScale,
+ @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ if (mScreenshotLayer == null) {
+ // Can't do animation.
+ return false;
+ }
+
+ // TODO : Found a way to get right end luma and re-enable color frame animation.
+ // End luma value is very not stable so it will cause more flicker is we run background
+ // color frame animation.
+ //mEndLuma = getLumaOfSurfaceControl(mEndBounds, mSurfaceControl);
+
+ // Figure out how the screen has moved from the original rotation.
+ int delta = deltaRotation(mEndRotation, mStartRotation);
+ switch (delta) { /* Counter-Clockwise Rotations */
+ case Surface.ROTATION_0:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_0_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.rotation_animation_enter);
+ break;
+ case Surface.ROTATION_90:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_enter);
+ break;
+ case Surface.ROTATION_180:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_enter);
+ break;
+ case Surface.ROTATION_270:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_enter);
+ break;
+ }
+
+ mRotateExitAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+ mRotateExitAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mRotateExitAnimation.scaleCurrentDuration(animationScale);
+ mRotateEnterAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+ mRotateEnterAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
+ mTransaction = mTransactionPool.acquire();
+ startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ startScreenshotRotationAnimation(animations, finishCallback, mainExecutor, animExecutor);
+ //startColorAnimation(mTransaction, animationScale);
+
+ return true;
+ }
+
+ private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
+ startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
+ mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ }
+
+ private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
+ startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
+ mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ }
+
+ private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
+ int colorTransitionMs = mContext.getResources().getInteger(
+ R.integer.config_screen_rotation_color_transition);
+ final float[] rgbTmpFloat = new float[3];
+ final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
+ final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
+ final long duration = colorTransitionMs * (long) animationScale;
+ final Transaction t = mTransactionPool.acquire();
+
+ final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ // Animation length is already expected to be scaled.
+ va.overrideDurationScale(1.0f);
+ va.setDuration(duration);
+ va.addUpdateListener(animation -> {
+ final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+ final float fraction = currentPlayTime / va.getDuration();
+ applyColor(startColor, endColor, rgbTmpFloat, fraction, mBackColorSurface, t);
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+ t);
+ mTransactionPool.release(t);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+ t);
+ mTransactionPool.release(t);
+ }
+ });
+ animExecutor.execute(va::start);
+ }
+
+ public void kill() {
+ Transaction t = mTransaction != null ? mTransaction : mTransactionPool.acquire();
+ if (mAnimLeash.isValid()) {
+ t.remove(mAnimLeash);
+ }
+
+ if (mScreenshotLayer != null) {
+ if (mScreenshotLayer.isValid()) {
+ t.remove(mScreenshotLayer);
+ }
+ mScreenshotLayer = null;
+
+ if (mBackColorSurface != null) {
+ if (mBackColorSurface.isValid()) {
+ t.remove(mBackColorSurface);
+ }
+ mBackColorSurface = null;
+ }
+ }
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
+ /**
+ * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the
+ * luminance at the borders of the bitmap
+ * @return the average luminance of all the pixels at the borders of the bitmap
+ */
+ private static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) {
+ // Cannot read content from buffer with protected usage.
+ if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888
+ || hasProtectedContent(hardwareBuffer)) {
+ return 0;
+ }
+
+ ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(),
+ hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1);
+ ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace);
+ Image image = ir.acquireLatestImage();
+ if (image == null || image.getPlanes().length == 0) {
+ return 0;
+ }
+
+ Image.Plane plane = image.getPlanes()[0];
+ ByteBuffer buffer = plane.getBuffer();
+ int width = image.getWidth();
+ int height = image.getHeight();
+ int pixelStride = plane.getPixelStride();
+ int rowStride = plane.getRowStride();
+ float[] borderLumas = new float[2 * width + 2 * height];
+
+ // Grab the top and bottom borders
+ int l = 0;
+ for (int x = 0; x < width; x++) {
+ borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride);
+ borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride);
+ }
+
+ // Grab the left and right borders
+ for (int y = 0; y < height; y++) {
+ borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride);
+ borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride);
+ }
+
+ // Cleanup
+ ir.close();
+
+ // Oh, is this too simple and inefficient for you?
+ // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians
+ Arrays.sort(borderLumas);
+ return borderLumas[borderLumas.length / 2];
+ }
+
+ /**
+ * @return whether the hardwareBuffer passed in is marked as protected.
+ */
+ private static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) {
+ return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT;
+ }
+
+ private static float getPixelLuminance(ByteBuffer buffer, int x, int y,
+ int pixelStride, int rowStride) {
+ int offset = y * rowStride + x * pixelStride;
+ int pixel = 0;
+ pixel |= (buffer.get(offset) & 0xff) << 16; // R
+ pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G
+ pixel |= (buffer.get(offset + 2) & 0xff); // B
+ pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
+ return Color.valueOf(pixel).luminance();
+ }
+
+ /**
+ * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
+ * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace)
+ */
+ private static float getLumaOfSurfaceControl(Rect bounds, SurfaceControl surfaceControl) {
+ if (surfaceControl == null) {
+ return 0;
+ }
+
+ Rect crop = new Rect(0, 0, bounds.width(), bounds.height());
+ SurfaceControl.ScreenshotHardwareBuffer buffer =
+ SurfaceControl.captureLayers(surfaceControl, crop, 1);
+ if (buffer == null) {
+ return 0;
+ }
+
+ return getMedianBorderLuma(buffer.getHardwareBuffer(), buffer.getColorSpace());
+ }
+
+ private static void createRotationMatrix(int rotation, int width, int height,
+ Matrix outMatrix) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ outMatrix.reset();
+ break;
+ case Surface.ROTATION_90:
+ outMatrix.setRotate(90, 0, 0);
+ outMatrix.postTranslate(height, 0);
+ break;
+ case Surface.ROTATION_180:
+ outMatrix.setRotate(180, 0, 0);
+ outMatrix.postTranslate(width, height);
+ break;
+ case Surface.ROTATION_270:
+ outMatrix.setRotate(270, 0, 0);
+ outMatrix.postTranslate(0, width);
+ break;
+ }
+ }
+
+ private static void applyColor(int startColor, int endColor, float[] rgbFloat,
+ float fraction, SurfaceControl surface, SurfaceControl.Transaction t) {
+ final int color = (Integer) ArgbEvaluator.getInstance().evaluate(fraction, startColor,
+ endColor);
+ Color middleColor = Color.valueOf(color);
+ rgbFloat[0] = middleColor.red();
+ rgbFloat[1] = middleColor.green();
+ rgbFloat[2] = middleColor.blue();
+ if (surface.isValid()) {
+ t.setColor(surface, rgbFloat);
+ }
+ t.apply();
+ }
+}
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 60707ccdca30..1ca71af4d017 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
@@ -77,6 +77,12 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Transition type for launching 2 tasks simultaneously. */
public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
+ /** Transition type for exiting PIP via the Shell, via pressing the expand button. */
+ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
+ /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
+ public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
@@ -91,12 +97,13 @@ public class Transitions implements RemoteCallable<Transitions> {
private float mTransitionAnimationScaleSetting = 1.0f;
private static final class ActiveTransition {
- IBinder mToken = null;
- TransitionHandler mHandler = null;
- boolean mMerged = false;
- TransitionInfo mInfo = null;
- SurfaceControl.Transaction mStartT = null;
- SurfaceControl.Transaction mFinishT = null;
+ IBinder mToken;
+ TransitionHandler mHandler;
+ boolean mMerged;
+ boolean mAborted;
+ TransitionInfo mInfo;
+ SurfaceControl.Transaction mStartT;
+ SurfaceControl.Transaction mFinishT;
}
/** Keeps track of currently playing transitions in the order of receipt. */
@@ -382,7 +389,7 @@ public class Transitions implements RemoteCallable<Transitions> {
}
boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
- return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
+ return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
(wct, cb) -> onFinish(active.mToken, wct, cb));
}
@@ -416,17 +423,19 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Special version of finish just for dealing with no-op/invalid transitions. */
private void onAbort(IBinder transition) {
- final int activeIdx = findActiveTransition(transition);
- if (activeIdx < 0) return;
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation aborted due to no-op, notifying core %s", transition);
- mActiveTransitions.remove(activeIdx);
- mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
+ onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */);
}
private void onFinish(IBinder transition,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
+ onFinish(transition, wct, wctCB, false /* abort */);
+ }
+
+ private void onFinish(IBinder transition,
+ @Nullable WindowContainerTransaction wct,
+ @Nullable WindowContainerTransactionCallback wctCB,
+ boolean abort) {
int activeIdx = findActiveTransition(transition);
if (activeIdx < 0) {
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
@@ -434,28 +443,37 @@ public class Transitions implements RemoteCallable<Transitions> {
return;
} else if (activeIdx > 0) {
// This transition was merged.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
- transition);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged (abort=%b:"
+ + " %s", abort, transition);
final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mMerged = true;
+ active.mAborted = abort;
if (active.mHandler != null) {
active.mHandler.onTransitionMerged(active.mToken);
}
return;
}
+ mActiveTransitions.get(activeIdx).mAborted = abort;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation finished, notifying core %s", transition);
+ "Transition animation finished (abort=%b), notifying core %s", abort, transition);
// Merge all relevant transactions together
SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
final ActiveTransition toMerge = mActiveTransitions.get(iA);
if (!toMerge.mMerged) break;
+ // aborted transitions have no start/finish transactions
+ if (mActiveTransitions.get(iA).mStartT == null) break;
+ if (fullFinish == null) {
+ fullFinish = new SurfaceControl.Transaction();
+ }
// Include start. It will be a no-op if it was already applied. Otherwise, we need it
// to maintain consistent state.
fullFinish.merge(mActiveTransitions.get(iA).mStartT);
fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
}
- fullFinish.apply();
+ if (fullFinish != null) {
+ fullFinish.apply();
+ }
// Now perform all the finishes.
mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
@@ -464,6 +482,12 @@ public class Transitions implements RemoteCallable<Transitions> {
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
}
+ // sift through aborted transitions
+ while (mActiveTransitions.size() > activeIdx
+ && mActiveTransitions.get(activeIdx).mAborted) {
+ ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
+ }
if (mActiveTransitions.size() <= activeIdx) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
+ "finished");
@@ -494,6 +518,12 @@ public class Transitions implements RemoteCallable<Transitions> {
int mergeIdx = activeIdx + 1;
while (mergeIdx < mActiveTransitions.size()) {
ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
+ if (mergeCandidate.mAborted) {
+ // transition was aborted, so we can skip for now (still leave it in the list
+ // so that it gets cleaned-up in the right order).
+ ++mergeIdx;
+ continue;
+ }
if (mergeCandidate.mMerged) {
throw new IllegalStateException("Can't merge a transition after not-merging"
+ " a preceding one.");
@@ -566,12 +596,19 @@ public class Transitions implements RemoteCallable<Transitions> {
* Starts a transition animation. This is always called if handleRequest returned non-null
* for a particular transition. Otherwise, it is only called if no other handler before
* it handled the transition.
- *
+ * @param startTransaction the transaction given to the handler to be applied before the
+ * transition animation. Note the handler is expected to call on
+ * {@link SurfaceControl.Transaction#apply()} for startTransaction.
+ * @param finishTransaction the transaction given to the handler to be applied after the
+ * transition animation. Unlike startTransaction, the handler is NOT
+ * expected to apply this transaction. The Transition system will
+ * apply it when finishCallback is called.
* @param finishCallback Call this when finished. This MUST be called on main thread.
* @return true if transition was handled, false if not (falls-back to default).
*/
boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback);
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
new file mode 100644
index 000000000000..2c668ed3d84d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
@@ -0,0 +1,71 @@
+/*
+ * 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.wm.shell.transition;
+
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * Represents a surface that is displayed over a transition surface.
+ */
+class WindowThumbnail {
+
+ private SurfaceControl mSurfaceControl;
+
+ private WindowThumbnail() {}
+
+ /** Create a thumbnail surface and attach it over a parent surface. */
+ static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent,
+ HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) {
+ WindowThumbnail windowThumbnail = new WindowThumbnail();
+ windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ .setParent(parent)
+ .setName("WindowThumanil : " + parent.toString())
+ .setCallsite("WindowThumanil")
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .build();
+
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader);
+ t.setBuffer(windowThumbnail.mSurfaceControl, graphicBuffer);
+ t.setColorSpace(windowThumbnail.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
+ t.setLayer(windowThumbnail.mSurfaceControl, Integer.MAX_VALUE);
+ t.show(windowThumbnail.mSurfaceControl);
+ t.apply();
+
+ return windowThumbnail;
+ }
+
+ SurfaceControl getSurface() {
+ return mSurfaceControl;
+ }
+
+ /** Remove the thumbnail surface and release the surface. */
+ void destroy(SurfaceControl.Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ t.remove(mSurfaceControl);
+ t.apply();
+ mSurfaceControl.release();
+ mSurfaceControl = null;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 9dd25fe0e6fe..3ca5b9c38aff 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -25,11 +25,17 @@ package {
android_test {
name: "WMShellFlickerTests",
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
test_suites: ["device-tests"],
libs: ["android.test.runner"],
static_libs: [
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index c5b5b91d570b..b36468b7e9a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -16,97 +16,100 @@
package com.android.wm.shell.flicker
+import android.content.ComponentName
import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-fun FlickerTestParameter.appPairsDividerIsVisible() {
+fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
- this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+ this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.appPairsDividerIsInvisible() {
+fun FlickerTestParameter.appPairsDividerIsInvisibleAtEnd() {
assertLayersEnd {
- this.notContains(APP_PAIR_SPLIT_DIVIDER)
+ this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
}
}
fun FlickerTestParameter.appPairsDividerBecomesVisible() {
assertLayers {
- this.isInvisible(DOCKED_STACK_DIVIDER)
+ this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
- .isVisible(DOCKED_STACK_DIVIDER)
+ .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.dockedStackDividerIsVisible() {
+fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
assertLayersEnd {
- this.isVisible(DOCKED_STACK_DIVIDER)
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
assertLayers {
- this.isInvisible(DOCKED_STACK_DIVIDER)
+ this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
- .isVisible(DOCKED_STACK_DIVIDER)
+ .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
assertLayers {
- this.isVisible(DOCKED_STACK_DIVIDER)
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
- .isInvisible(DOCKED_STACK_DIVIDER)
+ .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.dockedStackDividerIsInvisible() {
+fun FlickerTestParameter.dockedStackDividerNotExistsAtEnd() {
assertLayersEnd {
- this.notContains(DOCKED_STACK_DIVIDER)
+ this.notContains(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
+fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
+ rotation: Int,
+ primaryComponent: ComponentName
+) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(primaryLayerName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryComponent)
.coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryLayerName: String
+ primaryComponent: ComponentName
) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- visibleRegion(primaryLayerName)
+ val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryComponent)
.coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible(
+fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryLayerName: String
+ secondaryComponent: ComponentName
) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(secondaryLayerName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(secondaryComponent)
.coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryLayerName: String
+ secondaryComponent: ComponentName
) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- visibleRegion(secondaryLayerName)
+ val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(secondaryComponent)
.coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 03b93c74233c..ff1a6e6d9d90 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+@file:JvmName("CommonConstants")
package com.android.wm.shell.flicker
-const val IME_WINDOW_NAME = "InputMethod"
+import android.content.ComponentName
+
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
-const val DOCKED_STACK_DIVIDER = "DockedStackDivider" \ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#") \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index ef9f7421fd60..19374ed04be5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@ class AppPairsTestCannotPairNonResizeableApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
}
}
@@ -85,15 +84,13 @@ class AppPairsTestCannotPairNonResizeableApps(
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Presubmit
@Test
- fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+ fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
@Presubmit
@Test
@@ -103,8 +100,8 @@ class AppPairsTestCannotPairNonResizeableApps(
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.defaultWindowName)
- isInvisible(primaryApp.defaultWindowName)
+ isVisible(nonResizeableApp.component)
+ isInvisible(primaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index db63c4c43523..46ee89295a4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,10 +24,10 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,10 +53,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ waitAppsShown(primaryApp, secondaryApp)
}
}
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -68,14 +71,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+ isVisible(primaryApp.component)
+ isVisible(secondaryApp.component)
}
}
@@ -83,10 +86,10 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(primaryApp.defaultWindowName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryApp.component)
.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
- visibleRegion(secondaryApp.defaultWindowName)
+ visibleRegion(secondaryApp.component)
.coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index c8d34237231c..f7ced71afe8a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@ class AppPairsTestSupportPairNonResizeableApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
}
}
@@ -77,6 +76,10 @@ class AppPairsTestSupportPairNonResizeableApps(
resetMultiWindowConfig(instrumentation)
}
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -87,7 +90,7 @@ class AppPairsTestSupportPairNonResizeableApps(
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Presubmit
@Test
@@ -97,8 +100,8 @@ class AppPairsTestSupportPairNonResizeableApps(
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.defaultWindowName)
- isVisible(primaryApp.defaultWindowName)
+ isVisible(nonResizeableApp.component)
+ isVisible(primaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 83df83600d11..3debdd3276e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -25,10 +25,10 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,9 +51,11 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
get() = {
super.transition(this, it)
setup {
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ eachRun {
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+ waitAppsShown(primaryApp, secondaryApp)
+ }
}
transitions {
// TODO pair apps through normal UX flow
@@ -73,14 +75,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Presubmit
@Test
- fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+ fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
@Presubmit
@Test
fun bothAppWindowsInvisible() {
testSpec.assertWmEnd {
- isInvisible(primaryApp.defaultWindowName)
- isInvisible(secondaryApp.defaultWindowName)
+ isInvisible(primaryApp.component)
+ isInvisible(secondaryApp.component)
}
}
@@ -88,10 +90,10 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Test
fun appsStartingBounds() {
testSpec.assertLayersStart {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(primaryApp.defaultWindowName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryApp.component)
.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
- visibleRegion(secondaryApp.defaultWindowName)
+ visibleRegion(secondaryApp.component)
.coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
@@ -100,16 +102,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
- notContains(primaryApp.defaultWindowName)
- notContains(secondaryApp.defaultWindowName)
+ notContains(primaryApp.component)
+ notContains(secondaryApp.component)
}
}
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 1935bb97849c..cdf89a57fde8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -30,14 +30,14 @@ import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.BaseAppHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -154,26 +154,26 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter)
@FlakyTest(bugId = 186510496)
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible()
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible()
+ open fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
}
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index c875c0006703..3e782e608c86 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -28,10 +27,10 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -57,41 +56,43 @@ class RotateTwoLaunchedAppsInAppPairsMode(
transitions {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, true /* pair */))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ waitAppsShown(primaryApp, secondaryApp)
setRotation(testSpec.config.endRotation)
}
}
- @FlakyTest
+ @Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
+ isVisible(primaryApp.component)
+ .isVisible(secondaryApp.component)
}
}
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
- @FlakyTest(bugId = 172776659)
+ @Presubmit
@Test
- fun appPairsPrimaryBoundsIsVisible() =
- testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
- primaryApp.defaultWindowName)
+ fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ primaryApp.component)
- @FlakyTest(bugId = 172776659)
+ @FlakyTest
@Test
- fun appPairsSecondaryBoundsIsVisible() =
- testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
- secondaryApp.defaultWindowName)
+ fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ secondaryApp.component)
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index c3360ca0f7d3..ee28c7aa6beb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -28,12 +27,10 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -60,48 +57,50 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
this.setRotation(testSpec.config.endRotation)
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ waitAppsShown(primaryApp, secondaryApp)
}
}
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Presubmit
@Test
- override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
@Presubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+ isVisible(primaryApp.component)
+ isVisible(secondaryApp.component)
}
}
@FlakyTest(bugId = 172776659)
@Test
- fun appPairsPrimaryBoundsIsVisible() =
- testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
- primaryApp.defaultWindowName)
+ fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ primaryApp.component)
@FlakyTest(bugId = 172776659)
@Test
- fun appPairsSecondaryBoundsIsVisible() =
- testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
- secondaryApp.defaultWindowName)
+ fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ secondaryApp.component)
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 512fd9a58ea8..b95193a17265 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -22,7 +22,10 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
+import org.junit.Before
import org.junit.Test
abstract class RotateTwoLaunchedAppsTransition(
@@ -37,8 +40,8 @@ abstract class RotateTwoLaunchedAppsTransition(
test {
device.wakeUpAndGoToHomeScreen()
this.setRotation(Surface.ROTATION_0)
- primaryApp.launchViaIntent()
- secondaryApp.launchViaIntent()
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
updateTasksId()
}
}
@@ -52,10 +55,17 @@ abstract class RotateTwoLaunchedAppsTransition(
}
}
+ @Before
+ override fun setup() {
+ // AppPairs hasn't been updated to Shell Transition. There will be conflict on rotation.
+ assumeFalse(isShellTransitionsEnabled())
+ super.setup()
+ }
+
@FlakyTest
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsVisible() {
+ super.navBarLayerIsVisible()
}
@FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5b8cfb81016a..5a438af0b1f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -19,6 +19,7 @@ package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
import android.content.ComponentName
import android.graphics.Region
+import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
class AppPairsHelper(
@@ -43,5 +44,17 @@ class AppPairsHelper(
companion object {
const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
+
+ fun Flicker.waitAppsShown(app1: SplitScreenHelper?, app2: SplitScreenHelper?) {
+ wmHelper.waitFor("primaryAndSecondaryAppsVisible") { dump ->
+ val primaryAppVisible = app1?.let {
+ dump.wmState.isWindowSurfaceShown(app1.defaultWindowName)
+ } ?: false
+ val secondaryAppVisible = app2?.let {
+ dump.wmState.isWindowSurfaceShown(app2.defaultWindowName)
+ } ?: false
+ primaryAppVisible && secondaryAppVisible
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 4fe69ad7fabe..f15044ef37af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -20,6 +20,7 @@ import android.app.Instrumentation
import android.content.ComponentName
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import android.os.SystemProperties
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.util.Log
import androidx.test.uiautomator.By
@@ -60,6 +61,9 @@ abstract class BaseAppHelper(
companion object {
private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
+ fun isShellTransitionsEnabled() =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false)
+
fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
try {
SystemUtil.runShellCommand(instrumentation, cmd)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index cac46fe676b3..086e8b792e0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -61,7 +61,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+ require(wmHelper.waitImeShown()) { "IME did not appear" }
}
}
@@ -78,7 +78,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (wmHelper == null) {
uiDevice.waitForIdle()
} else {
- require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+ require(wmHelper.waitImeGone()) { "IME did did not close" }
}
} else {
// While pressing the back button should close the IME on TV as well, it may also lead
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index f4dd7decb1b7..d4b4e5daf7cb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -62,7 +62,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
stringExtras: Map<String, String>
) {
super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
- wmHelper.waitFor { it.wmState.hasPipWindow() }
+ wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
}
private fun focusOnObject(selector: BySelector): Boolean {
@@ -84,7 +84,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
clickObject(ENTER_PIP_BUTTON_ID)
// Wait on WMHelper or simply wait for 3 seconds
- wmHelper?.waitFor { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+ wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
}
fun clickStartMediaSessionButton() {
@@ -137,7 +137,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
// Wait for animation to complete.
- wmHelper.waitFor { !it.wmState.hasPipWindow() }
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForHomeActivityVisible()
}
@@ -167,7 +167,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
val windowRect = windowRegion.bounds
uiDevice.click(windowRect.centerX(), windowRect.centerY())
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- wmHelper.waitFor { !it.wmState.hasPipWindow() }
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 4f12f2bb9f5f..508e93988aa6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,22 +16,24 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -60,16 +62,16 @@ class EnterSplitScreenDockActivity(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
+ splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
@@ -77,27 +79,39 @@ class EnterSplitScreenDockActivity(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
}
}
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+ supportedRotations = listOf(Surface.ROTATION_0), // bugId = 179116910
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index f91f634a00e5..12f3909b6c34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -25,7 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,24 +62,34 @@ class EnterSplitScreenFromDetachedRecentTask(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- splitScreenApp.defaultWindowName)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 85ded8a45233..ac85c4857c76 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,17 +24,16 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -62,22 +62,22 @@ class EnterSplitScreenLaunchToSide(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+ secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackSecondaryBoundsIsVisible() =
- testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
- secondaryApp.defaultWindowName)
+ fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ secondaryApp.component)
@Presubmit
@Test
@@ -85,15 +85,35 @@ class EnterSplitScreenLaunchToSide(
@Presubmit
@Test
- fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ // when the app is launched, first the activity becomes visible, then the
+ // SnapshotStartingWindow appears and then the app window becomes visible.
+ // Because we log WM once per frame, sometimes the activity and the window
+ // become visible in the same entry, sometimes not, thus it is not possible to
+ // assert the visibility of the activity here
+ this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ .then()
+ // during re-parenting, the window may disappear and reappear from the
+ // trace, this occurs because we log only 1x per frame
+ .notContains(secondaryApp.component, isOptional = true)
+ .then()
+ .isAppWindowVisible(secondaryApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index e958bf39930e..964af2341439 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,12 +71,12 @@ class EnterSplitScreenNotSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
@@ -91,7 +92,12 @@ class EnterSplitScreenNotSupportNonResizable(
@Presubmit
@Test
- fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index d3acc82121b0..1b8afa668802 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -67,12 +68,12 @@ class EnterSplitScreenSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
@@ -88,16 +89,21 @@ class EnterSplitScreenSupportNonResizable(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index bad46836dcb7..247965f8071d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -24,15 +25,13 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -67,31 +66,52 @@ class ExitLegacySplitScreenFromBottom(
}
}
transitions {
- device.exitSplitScreenFromBottom()
+ device.exitSplitScreenFromBottom(wmHelper)
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ splitScreenApp.component, secondaryApp.component,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
- @Presubmit
+ @Postsubmit
@Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER)
+ fun layerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
+ .then()
+ .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
+ }
+ }
@FlakyTest
@Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesInVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(secondaryApp.component)
+ .then()
+ .isAppWindowInvisible(secondaryApp.component)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
- @Presubmit
+ @Postsubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
- @Presubmit
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 76dcd8b89242..af99fc4af1a0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -24,15 +26,13 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -71,31 +71,52 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ splitScreenApp.component, secondaryApp.component,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
- fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
@FlakyTest
@Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ fun layerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(splitScreenApp.component)
+ .then()
+ .isInvisible(splitScreenApp.component)
+ }
+ }
@FlakyTest
@Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ fun appWindowBecomesInVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(splitScreenApp.component)
+ .then()
+ .isAppWindowInvisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index d0a64b3774c7..95e4085db4eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,15 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -72,11 +69,11 @@ class LegacySplitScreenFromIntentNotSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ nonResizeableApp.component, splitScreenApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -92,44 +89,110 @@ class LegacySplitScreenFromIntentNotSupportNonResizable(
@Presubmit
@Test
- fun resizableAppLayerBecomesInvisible() =
- testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ fun resizableAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(splitScreenApp.component)
+ .then()
+ .isInvisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.notContains(nonResizeableApp.component)
+ .then()
+ .isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
+ /**
+ * Assets that [splitScreenApp] exists at the start of the trace and, once it becomes
+ * invisible, it remains invisible until the end of the trace.
+ */
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun resizableAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ // when the activity gets PAUSED the window may still be marked as visible
+ // it will be updated in the next log entry. This occurs because we record 1x
+ // per frame, thus ignore activity check here
+ this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ }
+ }
+ /**
+ * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+ * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+ * visible, it remains visible until the end of the trace.
+ */
@Presubmit
@Test
- fun resizableAppWindowBecomesInvisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.notContains(nonResizeableApp.component)
+ .then()
+ // we log once per frame, upon logging, window may be visible or not depending
+ // on what was processed until that moment. Both behaviors are correct
+ .isAppWindowInvisible(nonResizeableApp.component,
+ ignoreActivity = true, isOptional = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ }
+ }
+ /**
+ * Asserts that both the app window and the activity are visible at the end of the trace
+ */
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ this.isVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
@Presubmit
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index c26c05fa8db6..65346aa8ea5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,13 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,11 +69,11 @@ class LegacySplitScreenFromIntentSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ nonResizeableApp.component, splitScreenApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -90,27 +89,60 @@ class LegacySplitScreenFromIntentSupportNonResizable(
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
+ /**
+ * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+ * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+ * visible, it remains visible until the end of the trace.
+ */
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.notContains(nonResizeableApp.component)
+ .then()
+ // we log once per frame, upon logging, window may be visible or not depending
+ // on what was processed until that moment. Both behaviors are correct
+ .isAppWindowInvisible(nonResizeableApp.component,
+ ignoreActivity = true, isOptional = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index fb1758975442..547341a14cdd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,16 +25,12 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -73,11 +71,11 @@ class LegacySplitScreenFromRecentNotSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -93,37 +91,73 @@ class LegacySplitScreenFromRecentNotSupportNonResizable(
@Presubmit
@Test
- fun resizableAppLayerBecomesInvisible() =
- testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ fun resizableAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(splitScreenApp.component)
+ .then()
+ .isInvisible(splitScreenApp.component)
+ }
+ }
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun resizableAppWindowBecomesInvisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ fun resizableAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ // when the activity gets PAUSED the window may still be marked as visible
+ // it will be updated in the next log entry. This occurs because we record 1x
+ // per frame, thus ignore activity check here
+ this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ }
+ }
- @Presubmit
+ @Postsubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(nonResizeableApp.component)
+ .then()
+ .isAppWindowVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
@Presubmit
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index a9c28efcdf44..3f86658297fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,14 +24,12 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -71,11 +70,11 @@ class LegacySplitScreenFromRecentSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -91,27 +90,60 @@ class LegacySplitScreenFromRecentSupportNonResizable(
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ // when the app is launched, first the activity becomes visible, then the
+ // SnapshotStartingWindow appears and then the app window becomes visible.
+ // Because we log WM once per frame, sometimes the activity and the window
+ // become visible in the same entry, sometimes not, thus it is not possible to
+ // assert the visibility of the activity here
+ this.isAppWindowInvisible(nonResizeableApp.component, ignoreActivity = true)
+ .then()
+ // during re-parenting, the window may disappear and reappear from the
+ // trace, this occurs because we log only 1x per frame
+ .notContains(nonResizeableApp.component, isOptional = true)
+ .then()
+ // if the window reappears after re-parenting it will most likely not
+ // be visible in the first log entry (because we log only 1x per frame)
+ .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
+ .then()
+ .isAppWindowVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index a4d2ab51e358..7b4b71b41967 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -27,20 +27,18 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
@@ -62,8 +60,6 @@ import org.junit.runners.Parameterized
class LegacySplitScreenToLauncher(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
private val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
@@ -90,25 +86,25 @@ class LegacySplitScreenToLauncher(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
@Presubmit
@Test
@@ -122,19 +118,39 @@ class LegacySplitScreenToLauncher(
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
- @Presubmit
+ @Postsubmit
@Test
fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
+ @Postsubmit
+ @Test
+ fun layerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
@Presubmit
@Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage())
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 151179149)
+ @Presubmit
@Test
- fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index e8d4d1e9ada2..666d259f2b40 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
+import android.content.ComponentName
import android.content.Context
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
@@ -32,10 +33,12 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
@@ -46,12 +49,15 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
+ protected val LAUNCHER_COMPONENT = ComponentName("",
+ LauncherStrategyFactory.getInstance(instrumentation)
+ .launcherStrategy.supportedLauncherPackage)
private var prevDevEnableNonResizableMultiWindow = 0
@Before
open fun setup() {
+ // Legacy split is having some issue with Shell transition, and will be deprecated soon.
+ assumeFalse(isShellTransitionsEnabled())
prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
if (prevDevEnableNonResizableMultiWindow != 0) {
// Turn off the development option
@@ -70,8 +76,9 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
*
* b/182720234
*/
- open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ open val ignoredWindows: List<ComponentName> = listOf(
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -138,9 +145,9 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
}
companion object {
- internal const val LIVE_WALLPAPER_PACKAGE_NAME =
- "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
- internal const val LETTERBOX_NAME = "Letterbox"
- internal const val TOAST_NAME = "Toast"
+ internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+ "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
+ internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
+ internal val TOAST_COMPONENT = ComponentName("", "Toast")
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 05eb5f49a641..ec0c73a58846 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -24,14 +25,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -62,22 +60,28 @@ class OpenAppToLegacySplitScreen(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@FlakyTest
@Test
- fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage())
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(splitScreenApp.component)
+ .then()
+ .isAppWindowVisible(splitScreenApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
@@ -85,12 +89,27 @@ class OpenAppToLegacySplitScreen(
@FlakyTest
@Test
- fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage())
+ fun layerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(splitScreenApp.component)
+ .then()
+ .isVisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges(splitScreenApp.`package`,
+ "recents_animation_input_consumer", "NexusLauncherActivity")
+ }
+ }
- @FlakyTest(bugId = 151179149)
+ @Presubmit
@Test
- fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity")
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 3e83b6382939..d7f71a83ba7e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -28,23 +28,23 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
+import com.android.wm.shell.flicker.testapp.Components
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -101,16 +101,16 @@ class ResizeLegacySplitScreen(
}
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@FlakyTest(bugId = 156223549)
@Test
fun topAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.showsAppWindow(sSimpleActivity)
+ this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
}
}
@@ -118,18 +118,18 @@ class ResizeLegacySplitScreen(
@Test
fun bottomAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.showsAppWindow(sImeActivity)
+ this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
}
}
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
@Test
fun navBarLayerRotatesAndScales() =
@@ -142,21 +142,21 @@ class ResizeLegacySplitScreen(
@Test
fun topAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(sSimpleActivity)
+ this.isVisible(Components.SimpleActivity.COMPONENT)
}
}
@Test
fun bottomAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(sImeActivity)
+ this.isVisible(Components.ImeActivity.COMPONENT)
}
}
@Test
fun dividerLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(DOCKED_STACK_DIVIDER)
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
@@ -166,7 +166,7 @@ class ResizeLegacySplitScreen(
testSpec.assertLayersStart {
val displayBounds = WindowUtils.displayBounds
val dividerBounds =
- entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+ layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
val topAppBounds = Region(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -174,8 +174,8 @@ class ResizeLegacySplitScreen(
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion("SimpleActivity").coversExactly(topAppBounds)
- visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
}
}
@@ -185,7 +185,7 @@ class ResizeLegacySplitScreen(
testSpec.assertLayersStart {
val displayBounds = WindowUtils.displayBounds
val dividerBounds =
- entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+ layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
val topAppBounds = Region(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -194,8 +194,8 @@ class ResizeLegacySplitScreen(
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
- visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
}
}
@@ -207,8 +207,6 @@ class ResizeLegacySplitScreen(
}
companion object {
- private const val sSimpleActivity = "SimpleActivity"
- private const val sImeActivity = "ImeActivity"
private val startRatio = Rational(1, 3)
private val stopRatio = Rational(2, 3)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 58482eaae3f5..8a2b55b6fce0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -24,18 +24,17 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -66,21 +65,21 @@ class RotateOneLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -88,16 +87,26 @@ class RotateOneLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@FlakyTest
@Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(splitScreenApp.component)
+ .then()
+ .isAppWindowVisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 06828d6adb26..b3251573c942 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -24,18 +24,17 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -66,35 +65,45 @@ class RotateOneLaunchedAppInSplitScreenMode(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible(
- testSpec.config.startRotation, splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
+ testSpec.config.startRotation, splitScreenApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
testSpec.config.startRotation, testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
testSpec.config.startRotation, testSpec.config.endRotation)
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@FlakyTest
@Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(splitScreenApp.component)
+ .then()
+ .isAppWindowVisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index f8e32bf171d8..2be693631b26 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -18,26 +18,24 @@ package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -69,42 +67,66 @@ class RotateTwoLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackSecondaryBoundsIsVisible() =
- testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
- secondaryApp.defaultWindowName)
+ fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ secondaryApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
testSpec.config.startRotation, testSpec.config.endRotation)
@Presubmit
@Test
- fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ // when the app is launched, first the activity becomes visible, then the
+ // SnapshotStartingWindow appears and then the app window becomes visible.
+ // Because we log WM once per frame, sometimes the activity and the window
+ // become visible in the same entry, sometimes not, thus it is not possible to
+ // assert the visibility of the activity here
+ this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ .then()
+ // during re-parenting, the window may disappear and reappear from the
+ // trace, this occurs because we log only 1x per frame
+ .notContains(secondaryApp.component, isOptional = true)
+ .then()
+ // if the window reappears after re-parenting it will most likely not
+ // be visible in the first log entry (because we log only 1x per frame)
+ .isAppWindowInvisible(secondaryApp.component, isOptional = true)
+ .then()
+ .isAppWindowVisible(secondaryApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index cb246ca0b694..5782f145c00f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -24,20 +24,19 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -74,27 +73,27 @@ class RotateTwoLaunchedAppInSplitScreenMode(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackSecondaryBoundsIsVisible() =
- testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
- secondaryApp.defaultWindowName)
+ fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ secondaryApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -102,16 +101,31 @@ class RotateTwoLaunchedAppInSplitScreenMode(
@FlakyTest
@Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(secondaryApp.component)
+ .then()
+ .isAppWindowVisible(secondaryApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
index 2a660747bc1d..443204c245db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -16,4 +16,4 @@
package com.android.wm.shell.flicker.pip
-internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
+internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 00e50e7fe3b5..6488e2f7ab57 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -62,7 +62,7 @@ class EnterExitPipTest(
@Test
fun pipAppRemainInsideVisibleBounds() {
testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -70,10 +70,13 @@ class EnterExitPipTest(
@Test
fun showBothAppWindowsThenHidePip() {
testSpec.assertWm {
- showsAppWindow(testApp.defaultWindowName)
- .showsAppWindowOnTop(pipApp.defaultWindowName)
+ // when the activity is STOPPING, sometimes it becomes invisible in an entry before
+ // the window, sometimes in the same entry. This occurs because we log 1x per frame
+ // thus we ignore activity here
+ isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .isAppWindowOnTop(pipApp.component)
.then()
- .hidesAppWindow(testApp.defaultWindowName)
+ .isAppWindowInvisible(testApp.component)
}
}
@@ -81,10 +84,10 @@ class EnterExitPipTest(
@Test
fun showBothAppLayersThenHidePip() {
testSpec.assertLayers {
- isVisible(testApp.defaultWindowName)
- .isVisible(pipApp.defaultWindowName)
+ isVisible(testApp.component)
+ .isVisible(pipApp.component)
.then()
- .isInvisible(testApp.defaultWindowName)
+ .isInvisible(testApp.component)
}
}
@@ -92,8 +95,8 @@ class EnterExitPipTest(
@Test
fun testAppCoversFullScreenWithPipOnDisplay() {
testSpec.assertLayersStart {
- visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
- visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
+ visibleRegion(testApp.component).coversExactly(displayBounds)
+ visibleRegion(pipApp.component).coversAtMost(displayBounds)
}
}
@@ -101,7 +104,7 @@ class EnterExitPipTest(
@Test
fun pipAppCoversFullScreen() {
testSpec.assertLayersEnd {
- visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
+ visibleRegion(pipApp.component).coversExactly(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index b6af26060050..0f0a4abe30ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -44,30 +44,24 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
transitions {
- pipApp.clickEnterPipButton()
+ pipApp.clickEnterPipButton(wmHelper)
pipApp.expandPipWindow(wmHelper)
}
}
- @FlakyTest
- @Test
- override fun noUncoveredRegions() {
- super.noUncoveredRegions()
- }
-
@Presubmit
@Test
fun pipAppWindowAlwaysVisible() {
testSpec.assertWm {
- this.showsAppWindow(pipApp.defaultWindowName)
+ this.isAppWindowVisible(pipApp.component)
}
}
- @FlakyTest
+ @Presubmit
@Test
- fun pipLayerBecomesVisible() {
+ fun pipAppLayerAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(pipApp.windowName)
+ this.isVisible(pipApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 3a1456e53f87..67ad322f19f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -92,15 +92,13 @@ class EnterPipToOtherOrientationTest(
@FlakyTest
@Test
- override fun noUncoveredRegions() {
- super.noUncoveredRegions()
- }
+ override fun entireScreenCovered() = super.entireScreenCovered()
@Presubmit
@Test
fun pipAppWindowIsAlwaysOnTop() {
testSpec.assertWm {
- showsAppWindowOnTop(pipApp.defaultWindowName)
+ isAppWindowOnTop(pipApp.component)
}
}
@@ -108,7 +106,7 @@ class EnterPipToOtherOrientationTest(
@Test
fun pipAppHidesTestApp() {
testSpec.assertWmStart {
- isInvisible(testApp.defaultWindowName)
+ isInvisible(testApp.component)
}
}
@@ -116,7 +114,7 @@ class EnterPipToOtherOrientationTest(
@Test
fun testAppWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(testApp.defaultWindowName)
+ isVisible(testApp.component)
}
}
@@ -124,8 +122,8 @@ class EnterPipToOtherOrientationTest(
@Test
fun pipAppLayerHidesTestApp() {
testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
- isInvisible(testApp.defaultWindowName)
+ visibleRegion(pipApp.component).coversExactly(startingBounds)
+ isInvisible(testApp.component)
}
}
@@ -133,7 +131,7 @@ class EnterPipToOtherOrientationTest(
@Test
fun testAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
+ visibleRegion(testApp.component).coversExactly(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index eae7e973711c..28b1028d41ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -21,8 +21,8 @@ import android.view.Surface
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import org.junit.Test
@@ -47,9 +47,9 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
@Test
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
- this.showsAppWindow(PIP_WINDOW_TITLE)
+ this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
.then()
- .hidesAppWindow(PIP_WINDOW_TITLE)
+ .isAppWindowInvisible(pipApp.component)
}
}
@@ -57,15 +57,21 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
@Test
open fun pipLayerBecomesInvisible() {
testSpec.assertLayers {
- this.isVisible(PIP_WINDOW_TITLE)
+ this.isVisible(pipApp.component)
+ .isVisible(LAUNCHER_COMPONENT)
.then()
- .isInvisible(PIP_WINDOW_TITLE)
+ .isInvisible(pipApp.component)
+ .isVisible(LAUNCHER_COMPONENT)
}
}
@FlakyTest(bugId = 151179149)
@Test
- open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+ open fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
index cf84a2c696d0..1c5d77f68f43 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
@@ -48,13 +48,9 @@ class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTr
@FlakyTest
@Test
- override fun pipLayerBecomesInvisible() {
- super.pipLayerBecomesInvisible()
- }
+ override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
@FlakyTest
@Test
- override fun pipWindowBecomesInvisible() {
- super.pipWindowBecomesInvisible()
- }
+ override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
index 524a1b404591..356ec94b97e3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -56,19 +56,19 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition
@Presubmit
@Test
- override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Presubmit
@Test
- override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
@Presubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
@FlakyTest
@Test
@@ -85,7 +85,7 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition
@Presubmit
@Test
- override fun noUncoveredRegions() = super.noUncoveredRegions()
+ override fun entireScreenCovered() = super.entireScreenCovered()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index d88f94d5954a..5719413aff25 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.IME_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -79,7 +79,7 @@ class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
fun pipInVisibleBounds() {
testSpec.assertWm {
val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -90,7 +90,7 @@ class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
@Test
fun pipIsAboveAppWindow() {
testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+ isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 6833b96a802b..c09fdc203dbc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -46,7 +46,6 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 161435597)
@Group3
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
@@ -80,11 +79,11 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Presubmit
+ @FlakyTest(bugId = 161435597)
@Test
fun pipWindowInsideDisplayBounds() {
testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -92,25 +91,17 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(testApp.defaultWindowName)
- isVisible(imeApp.defaultWindowName)
- noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName)
+ isVisible(testApp.component)
+ isVisible(imeApp.component)
+ noWindowsOverlap(testApp.component, imeApp.component)
}
}
- @Presubmit
- @Test
- override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
+ @FlakyTest(bugId = 161435597)
@Test
fun pipLayerInsideDisplayBounds() {
testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -118,18 +109,14 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@Test
fun bothAppLayersVisible() {
testSpec.assertLayersEnd {
- visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
- visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
+ visibleRegion(testApp.component).coversAtMost(displayBounds)
+ visibleRegion(imeApp.component).coversAtMost(displayBounds)
}
}
- @Presubmit
- @Test
- override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
-
- @Presubmit
+ @FlakyTest(bugId = 161435597)
@Test
- override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+ override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
const val TEST_REPETITIONS = 2
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 d531af28e2ad..e0ec75778342 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
@@ -26,10 +26,10 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
@@ -73,9 +73,9 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
}
}
- @FlakyTest(bugId = 185400889)
+ @Presubmit
@Test
- override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
@FlakyTest
@@ -90,21 +90,21 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 185400889)
+ @Presubmit
@Test
fun appLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
- visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
- visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+ visibleRegion(fixedApp.component).coversExactly(startingBounds)
+ visibleRegion(pipApp.component).coversAtMost(startingBounds)
}
}
- @FlakyTest(bugId = 185400889)
+ @Presubmit
@Test
fun appLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
- visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
- visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
+ visibleRegion(fixedApp.component).coversExactly(endingBounds)
+ visibleRegion(pipApp.component).coversAtMost(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
index 1294ac93f647..914bc8b4d1c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
@@ -63,13 +63,13 @@ class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpe
@Presubmit
@Test
- fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) }
+ fun pipAlwaysVisible() = testSpec.assertWm { this.isAppWindowVisible(pipApp.component) }
@Presubmit
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
+ visibleRegion(pipApp.component).coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 55e5c4128967..5abcf39f3fbd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -22,9 +22,9 @@ import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import org.junit.FixMethodOrder
@@ -64,9 +64,11 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Test
fun appReplacesPipWindow() {
testSpec.assertWm {
- this.showsAppWindow(PIP_WINDOW_TITLE)
+ this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
+ .isAppWindowOnTop(pipApp.component)
.then()
- .showsAppWindowOnTop(pipApp.launcherName)
+ .invoke("hasNotPipWindow") { it.isNotPinned(pipApp.component) }
+ .isAppWindowOnTop(pipApp.component)
}
}
@@ -74,9 +76,11 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Test
fun appReplacesPipLayer() {
testSpec.assertLayers {
- this.isVisible(PIP_WINDOW_TITLE)
+ this.isVisible(pipApp.component)
+ .isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(pipApp.launcherName)
+ .isVisible(pipApp.component)
+ .isInvisible(LAUNCHER_COMPONENT)
}
}
@@ -84,22 +88,26 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Test
fun testAppCoversFullScreen() {
testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
+ visibleRegion(pipApp.component).coversExactly(displayBounds)
}
}
@FlakyTest(bugId = 151179149)
@Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity",
- pipApp.launcherName, "NexusLauncherActivity")
+ fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges("NexusLauncherActivity",
+ pipApp.launcherName, "NexusLauncherActivity")
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5)
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b4c75a6d1165..ca80d1837ea8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -20,25 +20,24 @@ import android.app.Instrumentation
import android.content.Intent
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.wm.shell.flicker.testapp.Components
import org.junit.Test
@@ -162,19 +161,19 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ open fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ open fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
@@ -188,6 +187,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun entireScreenCovered() =
+ testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 1f58bb2bf9db..f5856eea4899 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -93,7 +93,7 @@ class SetRequestedOrientationWhilePinnedTest(
@Test
fun pipWindowInsideDisplay() {
testSpec.assertWmStart {
- frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+ frameRegion(pipApp.component).coversAtMost(startingBounds)
}
}
@@ -101,7 +101,7 @@ class SetRequestedOrientationWhilePinnedTest(
@Test
fun pipAppShowsOnTop() {
testSpec.assertWmEnd {
- showsAppWindowOnTop(pipApp.defaultWindowName)
+ isAppWindowOnTop(pipApp.component)
}
}
@@ -109,28 +109,28 @@ class SetRequestedOrientationWhilePinnedTest(
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+ visibleRegion(pipApp.component).coversAtMost(startingBounds)
}
}
@Presubmit
@Test
fun pipAlwaysVisible() = testSpec.assertWm {
- this.showsAppWindow(pipApp.windowName)
+ this.isAppWindowVisible(pipApp.component)
}
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
+ visibleRegion(pipApp.component).coversExactly(endingBounds)
}
}
@FlakyTest
@Test
- override fun noUncoveredRegions() {
- super.noUncoveredRegions()
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 0110ba3f5b30..061218a015e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -37,14 +37,17 @@ class TvPipMenuTests : TvPipTestBase() {
private val systemUiResources =
packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
private val pipBoundsWhileInMenu: Rect = systemUiResources.run {
- val bounds = getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME))
+ val bounds = getString(getIdentifier("pip_menu_bounds", "string",
+ SYSTEM_UI_PACKAGE_NAME))
Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
}
private val playButtonDescription = systemUiResources.run {
- getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME))
+ getString(getIdentifier("pip_play", "string",
+ SYSTEM_UI_PACKAGE_NAME))
}
private val pauseButtonDescription = systemUiResources.run {
- getString(getIdentifier("pip_pause", "string", SYSTEM_UI_PACKAGE_NAME))
+ getString(getIdentifier("pip_pause", "string",
+ SYSTEM_UI_PACKAGE_NAME))
}
@Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 1b73920046dc..1c663409b913 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -70,7 +70,8 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? {
// descendant and then retrieve the element from the menu and return to the caller of this
// method.
val elementSelector = By.desc(desc)
- val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+ val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR)
+ .hasDescendant(elementSelector)
return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
?.findObject(elementSelector)
@@ -94,7 +95,8 @@ fun UiDevice.clickTvPipMenuFullscreenButton() {
}
fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
- focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+ focusOnAndClickTvPipMenuElement(By.desc(desc)
+ .pkg(SYSTEM_UI_PACKAGE_NAME)) ||
error("Could not focus on the Pip menu object with \"$desc\" description")
// So apparently Accessibility framework on TV is not very reliable and sometimes the state of
// the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 20ac5bf8fa84..1cbad155ba7b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -47,6 +47,8 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.HandlerExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
import org.junit.After;
import org.junit.Before;
@@ -71,6 +73,8 @@ public class TaskViewTest extends ShellTestCase {
ShellTaskOrganizer mOrganizer;
@Mock
HandlerExecutor mExecutor;
+ @Mock
+ SyncTransactionQueue mSyncQueue;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -99,7 +103,14 @@ public class TaskViewTest extends ShellTestCase {
}).when(mExecutor).execute(any());
when(mOrganizer.getExecutor()).thenReturn(mExecutor);
- mTaskView = new TaskView(mContext, mOrganizer);
+
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final TransactionRunnable r = invocationOnMock.getArgument(0);
+ r.runWithTransaction(new SurfaceControl.Transaction());
+ return null;
+ }).when(mSyncQueue).runInSync(any());
+
+ mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -112,7 +123,7 @@ public class TaskViewTest extends ShellTestCase {
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer);
+ TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 952dc31cdaee..e138595c47f3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -61,7 +61,7 @@ public class SplitLayoutTests extends ShellTestCase {
mSplitLayout = new SplitLayout(
"TestSplitLayout",
mContext,
- getConfiguration(false),
+ getConfiguration(),
mSplitLayoutHandler,
b -> b.setParent(mRootLeash),
mDisplayImeController,
@@ -71,9 +71,22 @@ public class SplitLayoutTests extends ShellTestCase {
@Test
@UiThreadTest
public void testUpdateConfiguration() {
- mSplitLayout.init();
- assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse();
- assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue();
+ final Configuration config = getConfiguration();
+
+ // Verify it returns true if new config won't affect split layout.
+ assertThat(mSplitLayout.updateConfiguration(config)).isFalse();
+
+ // Verify updateConfiguration returns true if the orientation changed.
+ config.orientation = ORIENTATION_LANDSCAPE;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if it rotated.
+ config.windowConfiguration.setRotation(1);
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the root bounds changed.
+ config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080));
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
}
@Test
@@ -108,12 +121,13 @@ public class SplitLayoutTests extends ShellTestCase {
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
}
- private static Configuration getConfiguration(boolean isLandscape) {
+ private static Configuration getConfiguration() {
final Configuration configuration = new Configuration();
configuration.unset();
- configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ configuration.orientation = ORIENTATION_PORTRAIT;
+ configuration.windowConfiguration.setRotation(0);
configuration.windowConfiguration.setBounds(
- new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
+ new Rect(0, 0, 1080, 2160));
return configuration;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 9d7c82bb8550..0270093da938 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -79,6 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
+ private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private ComponentName mComponent1;
@@ -90,11 +91,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
+ mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm());
mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
- mMockSyncTransactionQueue, mPipBoundsState,
+ mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 56a005642ce2..69ead3ac9cf9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -33,6 +33,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -46,7 +47,7 @@ import org.mockito.Spy;
/** Tests for {@link SideStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SideStageTests {
+public class SideStageTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@@ -60,8 +61,8 @@ public class SideStageTests {
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
- mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
- mSurfaceSession);
+ mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
+ mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
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 aca80f3556b9..b6da8681d850 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
@@ -102,7 +102,7 @@ public class SplitTransitionTests extends ShellTestCase {
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
- mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
@@ -131,6 +131,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
@@ -168,6 +169,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(newTask, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(accepted);
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -188,6 +190,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(newTask);
accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(accepted);
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -223,6 +226,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(mSideChild);
mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
@@ -244,6 +248,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -274,6 +279,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -298,6 +304,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction());
}
@@ -335,10 +342,11 @@ public class SplitTransitionTests extends ShellTestCase {
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback)
throws RemoteException {
mCalled = true;
- finishCallback.onTransitionFinished(mRemoteFinishWCT);
+ finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 90b5b37694c6..1a30f164f9a8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -21,11 +21,13 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
+import android.os.SystemProperties;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -52,6 +54,9 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests {
+ private static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
+
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@@ -93,6 +98,8 @@ public final class StageTaskListenerTests {
@Test
public void testChildTaskAppeared() {
+ // With shell transitions, the transition manages status changes, so skip this test.
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
@@ -110,6 +117,8 @@ public final class StageTaskListenerTests {
@Test
public void testTaskVanished() {
+ // With shell transitions, the transition manages status changes, so skip this test.
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
mStageTaskListener.mRootTaskInfo = mRootTask;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 2d2ab2c9f674..a2b1f6421f8d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -127,11 +127,13 @@ public class ShellTransitionTests {
TestTransitionHandler testHandler = new TestTransitionHandler() {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
for (TransitionInfo.Change chg : info.getChanges()) {
if (chg.getMode() == TRANSIT_CHANGE) {
- return super.startAnimation(transition, info, t, finishCallback);
+ return super.startAnimation(transition, info, startTransaction,
+ finishTransaction, finishCallback);
}
}
return false;
@@ -211,7 +213,7 @@ public class ShellTransitionTests {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -285,7 +287,7 @@ public class ShellTransitionTests {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(null /* wct */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
}
@Override
@@ -332,7 +334,7 @@ public class ShellTransitionTests {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -358,9 +360,11 @@ public class ShellTransitionTests {
oneShot.setTransition(transitToken);
IBinder anotherToken = new Binder();
assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0),
- mock(SurfaceControl.Transaction.class), testFinish));
+ mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+ testFinish));
assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0),
- mock(SurfaceControl.Transaction.class), testFinish));
+ mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+ testFinish));
}
@Test
@@ -477,7 +481,8 @@ public class ShellTransitionTests {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
mFinishes.add(finishCallback);
return true;
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b8fa55a18dac..18fab8ee5f92 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -88,6 +88,9 @@ bool Properties::enableWebViewOverlays = false;
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
+bool Properties::drawingEnabled = true;
+OverrideDrawingEnabled Properties::overrideDrawingEnabled = OverrideDrawingEnabled::Default;
+
bool Properties::load() {
bool prevDebugLayersUpdates = debugLayersUpdates;
bool prevDebugOverdraw = debugOverdraw;
@@ -141,6 +144,11 @@ bool Properties::load() {
enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, false);
+ drawingEnabled = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true);
+ if (!drawingEnabled) {
+ enableRTAnimations = false;
+ }
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
@@ -210,5 +218,18 @@ void Properties::overrideRenderPipelineType(RenderPipelineType type, bool inUnit
sRenderPipelineType = type;
}
+void Properties::setDrawingEnabled(bool newDrawingEnabled) {
+ overrideDrawingEnabled =
+ newDrawingEnabled ? OverrideDrawingEnabled::On : OverrideDrawingEnabled::Off;
+ enableRTAnimations = newDrawingEnabled;
+}
+
+bool Properties::isDrawingEnabled() {
+ if (overrideDrawingEnabled == OverrideDrawingEnabled::Default) {
+ return drawingEnabled;
+ }
+ return overrideDrawingEnabled == OverrideDrawingEnabled::On;
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7df6e2c92247..73fccb67800b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -187,6 +187,12 @@ enum DebugLevel {
*/
#define PROPERTY_WEBVIEW_OVERLAYS_ENABLED "debug.hwui.webview_overlays_enabled"
+/**
+ * Property for globally GL drawing state. Can be overridden per process with
+ * setDrawingEnabled.
+ */
+#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -208,6 +214,8 @@ enum class StretchEffectBehavior {
UniformScale // Uniform scale stretch everywhere
};
+enum class OverrideDrawingEnabled { Default, On, Off };
+
/**
* Renderthread-only singleton which manages several static rendering properties. Most of these
* are driven by system properties which are queried once at initialization, and again if init()
@@ -301,6 +309,12 @@ public:
stretchEffectBehavior = behavior;
}
+ // Represents if GL drawing is enabled. Should only be false in headless testing environments
+ static bool drawingEnabled;
+ static OverrideDrawingEnabled overrideDrawingEnabled;
+ static bool isDrawingEnabled();
+ static void setDrawingEnabled(bool enable);
+
private:
static StretchEffectBehavior stretchEffectBehavior;
static ProfileType sProfileType;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 442ae0fb2707..c945f274bbf4 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -15,6 +15,7 @@
*/
#include "RecordingCanvas.h"
+#include <hwui/Paint.h>
#include <GrRecordingContext.h>
@@ -495,7 +496,7 @@ struct DrawVectorDrawable final : Op {
sp<VectorDrawableRoot> mRoot;
SkRect mBounds;
- SkPaint paint;
+ Paint paint;
BitmapPalette palette;
};
@@ -833,7 +834,8 @@ constexpr color_transform_fn colorTransformForOp() {
// TODO: We should be const. Or not. Or just use a different map
// Unclear, but this is the quick fix
const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint), op->palette);
};
}
else if
@@ -842,7 +844,8 @@ constexpr color_transform_fn colorTransformForOp() {
// TODO: We should be const. Or not. Or just use a different map
// Unclear, but this is the quick fix
const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint));
};
}
else {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d032e2b00649..d6b6e162757c 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -182,7 +182,7 @@ int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
}
-void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) {
+void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const Paint& paint) {
while (mCanvas->getSaveCount() > restoreCount + 1) {
this->restore();
@@ -439,13 +439,13 @@ void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
mCanvas->drawColor(color, mode);
}
-void SkiaCanvas::onFilterPaint(SkPaint& paint) {
+void SkiaCanvas::onFilterPaint(Paint& paint) {
if (mPaintFilter) {
- mPaintFilter->filter(&paint);
+ mPaintFilter->filterFullPaint(&paint);
}
}
-void SkiaCanvas::drawPaint(const SkPaint& paint) {
+void SkiaCanvas::drawPaint(const Paint& paint) {
mCanvas->drawPaint(filterPaint(paint));
}
@@ -552,9 +552,8 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImage(image, left, top, sampling, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImage(image, left, top, p.sampling(), &p);
});
}
@@ -562,9 +561,8 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint*
auto image = bitmap.makeImage();
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImage(image, 0, 0, sampling, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImage(image, 0, 0, p.sampling(), &p);
});
}
@@ -575,18 +573,12 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
SkCanvas::kFast_SrcRectConstraint);
});
}
-static SkFilterMode paintToFilter(const Paint* paint) {
- return paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const Paint* paint) {
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
@@ -668,13 +660,13 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
if (paint) {
pnt = *paint;
}
- SkSamplingOptions sampling(paintToFilter(&pnt));
+ SkSamplingOptions sampling = pnt.sampling();
pnt.setShader(image->makeShader(sampling));
auto v = builder.detach();
- applyLooper(&pnt, [&](const SkPaint& p) {
+ applyLooper(&pnt, [&](const Paint& p) {
SkPaint copy(p);
- auto s = SkSamplingOptions(p.getFilterQuality());
+ auto s = p.sampling();
if (s != sampling) {
// applyLooper changed the quality?
copy.setShader(image->makeShader(s));
@@ -707,9 +699,8 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
auto image = bitmap.makeImage();
- applyLooper(paint, [&](const SkPaint& p) {
- auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
- mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImageLattice(image.get(), lattice, dst, p.filterMode(), &p);
});
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 438a40cb4c81..fd6bbdc55cf1 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -23,6 +23,7 @@
#include "VectorDrawable.h"
#include "hwui/Canvas.h"
#include "hwui/Paint.h"
+#include "hwui/BlurDrawLooper.h"
#include <SkCanvas.h>
#include "pipeline/skia/AnimatedDrawables.h"
@@ -73,7 +74,7 @@ public:
virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
- virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override;
+ virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) override;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) override;
@@ -99,7 +100,7 @@ public:
virtual SkCanvasState* captureCanvasState() const override;
virtual void drawColor(int color, SkBlendMode mode) override;
- virtual void drawPaint(const SkPaint& paint) override;
+ virtual void drawPaint(const Paint& paint) override;
virtual void drawPoint(float x, float y, const Paint& paint) override;
virtual void drawPoints(const float* points, int count, const Paint& paint) override;
@@ -167,10 +168,10 @@ protected:
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
- void onFilterPaint(SkPaint& paint);
+ void onFilterPaint(Paint& paint);
- SkPaint filterPaint(const SkPaint& src) {
- SkPaint dst(src);
+ Paint filterPaint(const Paint& src) {
+ Paint dst(src);
this->onFilterPaint(dst);
return dst;
}
@@ -179,21 +180,20 @@ protected:
template <typename Proc>
void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) {
BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr;
- const SkPaint* skpPtr = paint;
- SkPaint skp = skpPtr ? *skpPtr : SkPaint();
+ Paint pnt = paint ? *paint : Paint();
if (preFilter) {
- preFilter(skp);
+ preFilter(pnt);
}
- this->onFilterPaint(skp);
+ this->onFilterPaint(pnt);
if (looper) {
- looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
+ looper->apply(pnt, [&](SkPoint offset, const Paint& modifiedPaint) {
mCanvas->save();
mCanvas->translate(offset.fX, offset.fY);
proc(modifiedPaint);
mCanvas->restore();
});
} else {
- proc(skp);
+ proc(pnt);
}
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 55f434f49bbd..f116641b560f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -463,10 +463,10 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingCache.dirty = false;
}
- SkPaint skp;
+ Paint skp;
getPaintFor(&skp, mStagingProperties);
Paint paint;
- paint.setFilterQuality(skp.getFilterQuality());
+ paint.setFilterBitmap(skp.isFilterBitmap());
paint.setColorFilter(skp.refColorFilter());
paint.setAlpha(skp.getAlpha());
outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
@@ -476,9 +476,9 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingProperties.getBounds().bottom(), &paint);
}
-void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties& prop) const {
+void Tree::getPaintFor(Paint* outPaint, const TreeProperties& prop) const {
// HWUI always draws VD with bilinear filtering.
- outPaint->setFilterQuality(kLow_SkFilterQuality);
+ outPaint->setFilterBitmap(true);
if (prop.getColorFilter() != nullptr) {
outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
}
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index ac7d41e0d600..30bb04ae8361 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -648,7 +648,7 @@ public:
*/
void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint);
- void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
+ void getPaintFor(Paint* outPaint, const TreeProperties &props) const;
BitmapPalette computePalette();
void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 876f5c895c60..d08bc5c583c2 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -157,7 +157,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
lazyPaint.emplace();
lazyPaint->setAlpha(mProperties.mAlpha);
lazyPaint->setColorFilter(mProperties.mColorFilter);
- lazyPaint->setFilterQuality(kLow_SkFilterQuality);
}
canvas->concat(matrix);
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index 27a038d4598e..270d24af99fd 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -24,7 +24,7 @@ BlurDrawLooper::BlurDrawLooper(SkColor4f color, float blurSigma, SkPoint offset)
BlurDrawLooper::~BlurDrawLooper() = default;
-SkPoint BlurDrawLooper::apply(SkPaint* paint) const {
+SkPoint BlurDrawLooper::apply(Paint* paint) const {
paint->setColor(mColor);
if (mBlurSigma > 0) {
paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));
diff --git a/libs/hwui/hwui/BlurDrawLooper.h b/libs/hwui/hwui/BlurDrawLooper.h
index 7e6786f7dfbc..09a4e0f849b0 100644
--- a/libs/hwui/hwui/BlurDrawLooper.h
+++ b/libs/hwui/hwui/BlurDrawLooper.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
#define ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
-#include <SkPaint.h>
+#include <hwui/Paint.h>
#include <SkRefCnt.h>
class SkColorSpace;
@@ -30,10 +30,10 @@ public:
~BlurDrawLooper() override;
- // proc(SkPoint offset, const SkPaint& modifiedPaint)
+ // proc(SkPoint offset, const Paint& modifiedPaint)
template <typename DrawProc>
- void apply(const SkPaint& paint, DrawProc proc) const {
- SkPaint p(paint);
+ void apply(const Paint& paint, DrawProc proc) const {
+ Paint p(paint);
proc(this->apply(&p), p); // draw the shadow
proc({0, 0}, paint); // draw the original (on top)
}
@@ -43,7 +43,7 @@ private:
const float mBlurSigma;
const SkPoint mOffset;
- SkPoint apply(SkPaint* paint) const;
+ SkPoint apply(Paint* paint) const;
BlurDrawLooper(SkColor4f, float, SkPoint);
};
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 9023613478fc..70a558b3d69d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -162,7 +162,7 @@ public:
virtual int save(SaveFlags::Flags flags) = 0;
virtual void restore() = 0;
virtual void restoreToCount(int saveCount) = 0;
- virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0;
+ virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) = 0;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) = 0;
@@ -197,7 +197,7 @@ public:
// Canvas draw operations
// ----------------------------------------------------------------------------
virtual void drawColor(int color, SkBlendMode mode) = 0;
- virtual void drawPaint(const SkPaint& paint) = 0;
+ virtual void drawPaint(const Paint& paint) = 0;
// Geometry
virtual void drawPoint(float x, float y, const Paint& paint) = 0;
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 5d9fad5b676e..fc542c81a597 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -24,7 +24,6 @@
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkEncodedOrigin.h>
-#include <SkFilterQuality.h>
#include <SkPaint.h>
#undef LOG_TAG
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index d9c9eeed03e9..4a8f3e10fc26 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,13 +17,13 @@
#ifndef ANDROID_GRAPHICS_PAINT_H_
#define ANDROID_GRAPHICS_PAINT_H_
-#include "BlurDrawLooper.h"
#include "Typeface.h"
#include <cutils/compiler.h>
#include <SkFont.h>
#include <SkPaint.h>
+#include <SkSamplingOptions.h>
#include <string>
#include <minikin/FontFamily.h>
@@ -32,6 +32,8 @@
namespace android {
+class BlurDrawLooper;
+
class Paint : public SkPaint {
public:
// Default values for underlined and strikethrough text,
@@ -60,7 +62,7 @@ public:
const SkFont& getSkFont() const { return mFont; }
BlurDrawLooper* getLooper() const { return mLooper.get(); }
- void setLooper(sk_sp<BlurDrawLooper> looper) { mLooper = std::move(looper); }
+ void setLooper(sk_sp<BlurDrawLooper> looper);
// These shadow the methods on SkPaint, but we need to so we can keep related
// attributes in-sync.
@@ -138,7 +140,15 @@ public:
void setDevKern(bool d) { mDevKern = d; }
// Deprecated -- bitmapshaders will be taking this flag explicitly
- bool isFilterBitmap() const { return this->getFilterQuality() != kNone_SkFilterQuality; }
+ bool isFilterBitmap() const { return mFilterBitmap; }
+ void setFilterBitmap(bool filter) { mFilterBitmap = filter; }
+
+ SkFilterMode filterMode() const {
+ return mFilterBitmap ? SkFilterMode::kLinear : SkFilterMode::kNearest;
+ }
+ SkSamplingOptions sampling() const {
+ return SkSamplingOptions(this->filterMode());
+ }
// The Java flags (Paint.java) no longer fit into the native apis directly.
// These methods handle converting to and from them and the native representations
@@ -169,6 +179,7 @@ private:
// nullptr is valid: it means the default typeface.
const Typeface* mTypeface = nullptr;
Align mAlign = kLeft_Align;
+ bool mFilterBitmap = false;
bool mStrikeThru = false;
bool mUnderline = false;
bool mDevKern = false;
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
index 0e7b61977000..4996aa445316 100644
--- a/libs/hwui/hwui/PaintFilter.h
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -1,17 +1,18 @@
#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
#define ANDROID_GRAPHICS_PAINT_FILTER_H_
-class SkPaint;
+#include <SkRefCnt.h>
namespace android {
+class Paint;
+
class PaintFilter : public SkRefCnt {
public:
/**
* Called with the paint that will be used to draw.
* The implementation may modify the paint as they wish.
*/
- virtual void filter(SkPaint*) = 0;
virtual void filterFullPaint(Paint*) = 0;
};
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index fa2674fc2f5e..aac928f85924 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -15,6 +15,7 @@
*/
#include "Paint.h"
+#include "BlurDrawLooper.h"
namespace android {
@@ -43,6 +44,7 @@ Paint::Paint(const Paint& paint)
, mHyphenEdit(paint.mHyphenEdit)
, mTypeface(paint.mTypeface)
, mAlign(paint.mAlign)
+ , mFilterBitmap(paint.mFilterBitmap)
, mStrikeThru(paint.mStrikeThru)
, mUnderline(paint.mUnderline)
, mDevKern(paint.mDevKern) {}
@@ -62,6 +64,7 @@ Paint& Paint::operator=(const Paint& other) {
mHyphenEdit = other.mHyphenEdit;
mTypeface = other.mTypeface;
mAlign = other.mAlign;
+ mFilterBitmap = other.mFilterBitmap;
mStrikeThru = other.mStrikeThru;
mUnderline = other.mUnderline;
mDevKern = other.mDevKern;
@@ -77,6 +80,7 @@ bool operator==(const Paint& a, const Paint& b) {
a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
+ a.mFilterBitmap == b.mFilterBitmap &&
a.mStrikeThru == b.mStrikeThru && a.mUnderline == b.mUnderline &&
a.mDevKern == b.mDevKern;
}
@@ -88,11 +92,16 @@ void Paint::reset() {
mFont.setEdging(SkFont::Edging::kAlias);
mLooper.reset();
+ mFilterBitmap = false;
mStrikeThru = false;
mUnderline = false;
mDevKern = false;
}
+void Paint::setLooper(sk_sp<BlurDrawLooper> looper) {
+ mLooper = std::move(looper);
+}
+
void Paint::setAntiAlias(bool aa) {
// Java does not support/understand subpixel(lcd) antialiasing
SkASSERT(mFont.getEdging() != SkFont::Edging::kSubpixelAntiAlias);
@@ -131,9 +140,6 @@ static uint32_t paintToLegacyFlags(const SkPaint& paint) {
uint32_t flags = 0;
flags |= -(int)paint.isAntiAlias() & sAntiAliasFlag;
flags |= -(int)paint.isDither() & sDitherFlag;
- if (paint.getFilterQuality() != kNone_SkFilterQuality) {
- flags |= sFilterBitmapFlag;
- }
return flags;
}
@@ -150,12 +156,6 @@ static uint32_t fontToLegacyFlags(const SkFont& font) {
static void applyLegacyFlagsToPaint(uint32_t flags, SkPaint* paint) {
paint->setAntiAlias((flags & sAntiAliasFlag) != 0);
paint->setDither ((flags & sDitherFlag) != 0);
-
- if (flags & sFilterBitmapFlag) {
- paint->setFilterQuality(kLow_SkFilterQuality);
- } else {
- paint->setFilterQuality(kNone_SkFilterQuality);
- }
}
static void applyLegacyFlagsToFont(uint32_t flags, SkFont* font) {
@@ -182,18 +182,20 @@ void Paint::SetSkPaintJavaFlags(SkPaint* paint, uint32_t flags) {
uint32_t Paint::getJavaFlags() const {
uint32_t flags = paintToLegacyFlags(*this) | fontToLegacyFlags(mFont);
- flags |= -(int)mStrikeThru & sStrikeThruFlag;
- flags |= -(int)mUnderline & sUnderlineFlag;
- flags |= -(int)mDevKern & sDevKernFlag;
+ flags |= -(int)mStrikeThru & sStrikeThruFlag;
+ flags |= -(int)mUnderline & sUnderlineFlag;
+ flags |= -(int)mDevKern & sDevKernFlag;
+ flags |= -(int)mFilterBitmap & sFilterBitmapFlag;
return flags;
}
void Paint::setJavaFlags(uint32_t flags) {
applyLegacyFlagsToPaint(flags, this);
applyLegacyFlagsToFont(flags, &mFont);
- mStrikeThru = (flags & sStrikeThruFlag) != 0;
- mUnderline = (flags & sUnderlineFlag) != 0;
- mDevKern = (flags & sDevKernFlag) != 0;
+ mStrikeThru = (flags & sStrikeThruFlag) != 0;
+ mUnderline = (flags & sUnderlineFlag) != 0;
+ mDevKern = (flags & sDevKernFlag) != 0;
+ mFilterBitmap = (flags & sFilterBitmapFlag) != 0;
}
} // namespace android
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index bcec0fa8a1cc..22a1e1fd94b9 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -663,8 +663,7 @@ namespace PaintGlue {
}
static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) {
- reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
- filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+ reinterpret_cast<Paint*>(paintHandle)->setFilterBitmap(filterBitmap);
}
static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) {
diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
index ec115b4e141c..c30e29bf247f 100644
--- a/libs/hwui/jni/PaintFilter.cpp
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -29,10 +29,6 @@ public:
fClearFlags = static_cast<uint16_t>(clearFlags);
fSetFlags = static_cast<uint16_t>(setFlags);
}
- void filter(SkPaint* paint) override {
- uint32_t flags = Paint::GetSkPaintJavaFlags(*paint);
- Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags);
- }
void filterFullPaint(Paint* paint) override {
paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags);
}
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
index ac2f5b77d23a..1259bb5a4f65 100644
--- a/libs/hwui/jni/Utils.cpp
+++ b/libs/hwui/jni/Utils.cpp
@@ -18,6 +18,7 @@
#include "SkUtils.h"
#include "SkData.h"
+#include <inttypes.h>
#include <log/log.h>
using namespace android;
@@ -76,7 +77,7 @@ bool AssetStreamAdaptor::seek(size_t position) {
bool AssetStreamAdaptor::move(long offset) {
if (fAsset->seek(offset, SEEK_CUR) == -1) {
- SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset);
+ SkDebugf("---- fAsset->seek(%li, SEEK_CUR) failed\n", offset);
return false;
}
@@ -100,7 +101,7 @@ size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
}
off64_t newOffset = fAsset->seek(size, SEEK_CUR);
if (-1 == newOffset) {
- SkDebugf("---- fAsset->seek(%d) failed\n", size);
+ SkDebugf("---- fAsset->seek(%zu) failed\n", size);
return 0;
}
amount = newOffset - oldOffset;
@@ -127,14 +128,14 @@ sk_sp<SkData> android::CopyAssetToData(Asset* asset) {
const off64_t size = asset->getLength();
if (size <= 0) {
- SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
+ SkDebugf("---- copyAsset: asset->getLength() returned %" PRId64 "\n", size);
return NULL;
}
sk_sp<SkData> data(SkData::MakeUninitialized(size));
const off64_t len = asset->read(data->writable_data(), size);
if (len != size) {
- SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
+ SkDebugf("---- copyAsset: asset->read(%" PRId64 ") returned %" PRId64 "\n", size, len);
return NULL;
}
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index a611f7ce2d14..dada5ae960e2 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -233,7 +233,7 @@ static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorS
jlong colorLong, jint modeHandle) {
SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
- SkPaint p;
+ Paint p;
p.setColor4f(color, cs.get());
SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
@@ -421,7 +421,7 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
&filteredPaint);
@@ -443,7 +443,7 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHan
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, left, top, &filteredPaint);
} else {
canvas->drawBitmap(bitmap, left, top, paint);
@@ -458,7 +458,7 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHan
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
canvas->restore();
@@ -486,7 +486,7 @@ static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitma
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
} else {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c4cdb7db7d86..afe86cc2ab1d 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -898,6 +898,14 @@ static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
}
+static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) {
+ Properties::setDrawingEnabled(enabled);
+}
+
+static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) {
+ return Properties::isDrawingEnabled();
+}
+
// ----------------------------------------------------------------------------
// HardwareRendererObserver
// ----------------------------------------------------------------------------
@@ -1034,6 +1042,8 @@ static const JNINativeMethod gMethods[] = {
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
{"isWebViewOverlaysEnabled", "()Z",
(void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled},
+ {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled},
+ {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled},
};
static JavaVM* mJvm = nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 76c4a03d3a91..9c51e628e04a 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -187,28 +187,18 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
// kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
// older.
- if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+ if (sApiLevel <= 27 && paint.asBlendMode() == SkBlendMode::kClear) {
paint.setBlendMode(SkBlendMode::kDstOut);
}
}
-static SkFilterMode Paint_to_filter(const SkPaint& paint) {
- return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
-static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
- // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
- return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
-}
-
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
sk_sp<SkImage> image = bitmap.makeImage();
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
+ [&](const Paint& p) {
+ mRecorder.drawImage(image, left, top, p.sampling(), &p, bitmap.palette());
},
FilterForImage);
@@ -228,8 +218,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
+ [&](const Paint& p) {
+ mRecorder.drawImage(image, 0, 0, p.sampling(), &p, bitmap.palette());
},
FilterForImage);
@@ -248,8 +238,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
+ [&](const Paint& p) {
+ mRecorder.drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
},
FilterForImage);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 025be7b2b6c1..ab9d0b2244e2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -256,7 +256,7 @@ void CanvasContext::setStopped(bool stopped) {
}
void CanvasContext::allocateBuffers() {
- if (mNativeSurface) {
+ if (mNativeSurface && Properties::isDrawingEnabled()) {
ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
}
}
@@ -480,7 +480,8 @@ nsecs_t CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
- if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+ if (!Properties::isDrawingEnabled() ||
+ (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6dbfcc349d50..2fed4686f16e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -90,9 +90,17 @@ public:
* and false otherwise (e.g. cache limits have been exceeded).
*/
bool pinImages(std::vector<SkImage*>& mutableImages) {
+ if (!Properties::isDrawingEnabled()) {
+ return true;
+ }
return mRenderPipeline->pinImages(mutableImages);
}
- bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { return mRenderPipeline->pinImages(images); }
+ bool pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+ if (!Properties::isDrawingEnabled()) {
+ return true;
+ }
+ return mRenderPipeline->pinImages(images);
+ }
/**
* Unpin any image that had be previously pinned to the GPU cache
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 10ba07905c45..31a8ae1d38cd 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -49,7 +49,7 @@ public:
paint.setAntiAlias(true);
paint.setColor(Color::Green_700);
canvas.drawCircle(200, 200, 200, paint);
- SkPaint alphaPaint;
+ Paint alphaPaint;
alphaPaint.setAlpha(128);
canvas.restoreUnclippedLayer(unclippedSaveLayer, alphaPaint);
canvas.restore();
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index a1ba70a22581..dc1b2e668dd0 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -61,11 +61,11 @@ TEST(SkiaBehavior, lightingColorFilter_simplify) {
TEST(SkiaBehavior, porterDuffCreateIsCached) {
SkPaint paint;
paint.setBlendMode(SkBlendMode::kOverlay);
- auto expected = paint.getBlendMode();
+ auto expected = paint.asBlendMode();
paint.setBlendMode(SkBlendMode::kClear);
- ASSERT_NE(expected, paint.getBlendMode());
+ ASSERT_NE(expected, paint.asBlendMode());
paint.setBlendMode(SkBlendMode::kOverlay);
- ASSERT_EQ(expected, paint.getBlendMode());
+ ASSERT_EQ(expected, paint.asBlendMode());
}
TEST(SkiaBehavior, pathIntersection) {
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index a8f2d9a28d67..94bcb1110e05 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -32,13 +32,6 @@ namespace uirenderer {
*/
class PaintUtils {
public:
- static inline GLenum getFilter(const SkPaint* paint) {
- if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
- return GL_LINEAR;
- }
- return GL_NEAREST;
- }
-
static bool isOpaquePaint(const SkPaint* paint) {
if (!paint) return true; // default (paintless) behavior is SrcOver, black
@@ -48,7 +41,7 @@ public:
}
// Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
- SkBlendMode mode = paint->getBlendMode();
+ const auto mode = paint->asBlendMode();
return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
}
@@ -59,7 +52,7 @@ public:
}
static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
- return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
+ return paint ? paint->getBlendMode_or(SkBlendMode::kSrcOver) : SkBlendMode::kSrcOver;
}
static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 526b84e85e38..ccd979c095ce 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1569,6 +1569,7 @@ public class LocationManager {
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
public boolean injectLocation(@NonNull Location location) {
Preconditions.checkArgument(location != null, "invalid null location");
diff --git a/media/Android.bp b/media/Android.bp
index a66236e6f4ea..cf4a0b1005d7 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -8,23 +8,6 @@ package {
}
aidl_interface {
- name: "audio_common-aidl",
- unstable: true,
- host_supported: true,
- vendor_available: true,
- local_include_dir: "aidl",
- double_loadable: true,
- srcs: [
- "aidl/android/media/audio/common/AudioChannelMask.aidl",
- "aidl/android/media/audio/common/AudioConfig.aidl",
- "aidl/android/media/audio/common/AudioFormat.aidl",
- "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
- "aidl/android/media/audio/common/AudioStreamType.aidl",
- "aidl/android/media/audio/common/AudioUsage.aidl",
- ],
-}
-
-aidl_interface {
name: "media_permission-aidl",
unstable: true,
host_supported: true,
@@ -40,30 +23,92 @@ aidl_interface {
name: "soundtrigger_middleware-aidl",
unstable: true,
local_include_dir: "aidl",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ },
srcs: [
- "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl",
- "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
- "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl",
- "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
- "aidl/android/media/soundtrigger_middleware/Phrase.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundModel.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl",
"aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
- "aidl/android/media/soundtrigger_middleware/Status.aidl",
],
imports: [
- "audio_common-aidl",
+ "android.media.soundtrigger.types",
"media_permission-aidl",
],
}
+
+aidl_interface {
+ name: "android.media.audio.common.types",
+ vendor_available: true,
+ host_supported: true,
+ double_loadable: true,
+ flags: ["-Werror", "-Weverything", ],
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/audio/common/AudioChannelMask.aidl",
+ "aidl/android/media/audio/common/AudioConfig.aidl",
+ "aidl/android/media/audio/common/AudioFormat.aidl",
+ "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioStreamType.aidl",
+ "aidl/android/media/audio/common/AudioUsage.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
+
+aidl_interface {
+ name: "android.media.soundtrigger.types",
+ vendor_available: true,
+ flags: ["-Werror", "-Weverything", ],
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/soundtrigger/AudioCapabilities.aidl",
+ "aidl/android/media/soundtrigger/ConfidenceLevel.aidl",
+ "aidl/android/media/soundtrigger/ModelParameter.aidl",
+ "aidl/android/media/soundtrigger/ModelParameterRange.aidl",
+ "aidl/android/media/soundtrigger/Phrase.aidl",
+ "aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl",
+ "aidl/android/media/soundtrigger/PhraseSoundModel.aidl",
+ "aidl/android/media/soundtrigger/Properties.aidl",
+ "aidl/android/media/soundtrigger/RecognitionConfig.aidl",
+ "aidl/android/media/soundtrigger/RecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger/RecognitionMode.aidl",
+ "aidl/android/media/soundtrigger/RecognitionStatus.aidl",
+ "aidl/android/media/soundtrigger/SoundModel.aidl",
+ "aidl/android/media/soundtrigger/SoundModelType.aidl",
+ "aidl/android/media/soundtrigger/Status.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+ imports: [
+ "android.media.audio.common.types",
+ ],
+}
diff --git a/media/aidl/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
index b9b08e6921bc..17be8dd5461f 100644
--- a/media/aidl/android/media/audio/common/AudioChannelMask.aidl
+++ b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
@@ -57,8 +57,11 @@ package android.media.audio.common;
* checking the channel mask, the implementer should look for ways to fix it
* with additional information outside of the mask.
*
+ * TODO: this should be replaced with strings or other mechanisms that are easy to extend, in line
+ * with the audio HAL conventions.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioChannelMask {
/**
@@ -113,42 +116,43 @@ enum AudioChannelMask {
*/
OUT_HAPTIC_A = 0x20000000,
OUT_HAPTIC_B = 0x10000000,
-// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
-// OUT_MONO = OUT_FRONT_LEFT,
-// OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
-// OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-// OUT_QUAD_BACK = OUT_QUAD,
-// /**
-// * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
-// */
-// OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
-// OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
-// OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-// OUT_5POINT1_BACK = OUT_5POINT1,
-// /**
-// * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
-// */
-// OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-// OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
-// /**
-// * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
-// */
-// OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-// OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
-// OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
-// OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
-// OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
-// OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
+ OUT_MONO = OUT_FRONT_LEFT,
+ OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
+ OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+ OUT_QUAD_BACK = OUT_QUAD,
+ /**
+ * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
+ */
+ OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
+ OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
+ OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+ OUT_5POINT1_BACK = OUT_5POINT1,
+ /**
+ * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
+ */
+ OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+ OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
+ /**
+ * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
+ */
+ OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+ OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
+ OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
+ OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
+ OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+ OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
/**
* These are bits only, not complete values
*
diff --git a/media/aidl/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 50dd796e1fa0..d1285611f44e 100644
--- a/media/aidl/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
@@ -27,10 +27,12 @@ import android.media.audio.common.AudioOffloadInfo;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable AudioConfig {
int sampleRateHz;
int channelMask;
- AudioFormat format;
+ AudioFormat format = AudioFormat.INVALID;
AudioOffloadInfo offloadInfo;
long frameCount;
}
diff --git a/media/aidl/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl
index aadc8e26cce3..73fbca25a578 100644
--- a/media/aidl/android/media/audio/common/AudioFormat.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormat.aidl
@@ -32,6 +32,7 @@ package android.media.audio.common;
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioFormat {
INVALID = 0xFFFFFFFF,
diff --git a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index ec10d71135ae..7be5e6aadcb3 100644
--- a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
@@ -28,17 +28,19 @@ import android.media.audio.common.AudioUsage;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable AudioOffloadInfo {
int sampleRateHz;
int channelMask;
- AudioFormat format;
- AudioStreamType streamType;
+ AudioFormat format = AudioFormat.INVALID;
+ AudioStreamType streamType = AudioStreamType.INVALID;
int bitRatePerSecond;
long durationMicroseconds;
boolean hasVideo;
boolean isStreaming;
int bitWidth;
int bufferSize;
- AudioUsage usage;
+ AudioUsage usage = AudioUsage.INVALID;
}
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index c54566726350..8b7036705ac5 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -26,8 +26,14 @@ package android.media.audio.common;
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioStreamType {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -2,
DEFAULT = -1,
MIN = 0,
VOICE_CALL = 0,
diff --git a/media/aidl/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index ef348165b22c..028eefee180b 100644
--- a/media/aidl/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
@@ -22,8 +22,14 @@ package android.media.audio.common;
/**
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioUsage {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
UNKNOWN = 0,
MEDIA = 1,
VOICE_COMMUNICATION = 2,
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
index 36389047cee8..524978618ff5 100644
--- a/media/aidl/android/media/permission/Identity.aidl
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -20,6 +20,7 @@ package android.media.permission;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable Identity {
/** Linux user ID. */
int uid = -1;
diff --git a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
index 97a8849c7b07..7b0825b1e6cb 100644
--- a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
+++ b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* AudioCapabilities supported by the implemented HAL driver.
* @hide
*/
+@VintfStability
@Backing(type="int")
enum AudioCapabilities {
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
index 3dbc70556bd3..3fcba404e21c 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
+++ b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* A recognition confidence level.
@@ -21,6 +21,8 @@ package android.media.soundtrigger_middleware;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable ConfidenceLevel {
/** user ID. */
int userId;
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
index 09936278e93a..948400883736 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
+++ b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Model specific parameters to be used with parameter set and get APIs.
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum ModelParameter {
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
index d6948a87dc6d..e7c616bae9bb 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
+++ b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Value range for a model parameter.
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable ModelParameterRange {
/** Minimum (inclusive) */
int minInclusive;
diff --git a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger/Phrase.aidl
index 98a489f8a6a9..077db21586c0 100644
--- a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
+++ b/media/aidl/android/media/soundtrigger/Phrase.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Key phrase descriptor.
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable Phrase {
/** Unique keyphrase ID assigned at enrollment time. */
int id;
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
index 6a3ec61d1ebf..654f7c259d2f 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -13,16 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.RecognitionEvent;
/**
* An event that gets sent to indicate a phrase recognition (or aborting of the recognition
process).
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseRecognitionEvent {
/** Common recognition event. */
RecognitionEvent common;
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
index cb96bf37a95d..eb523eb0d396 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ConfidenceLevel;
/**
* Specialized recognition event for key phrase detection.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseRecognitionExtra {
- // TODO(ytai): Constants / enums.
-
- /** keyphrase ID */
+ /** Keyphrase ID */
int id;
- /** recognition modes used for this keyphrase */
+ /** Bitfield, indexed by RecognitionMode. */
int recognitionModes;
- /** confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
+ /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER. Value is between 0-100. */
int confidenceLevel;
- /** number of user confidence levels */
+ /** Number of user confidence levels */
ConfidenceLevel[] levels;
}
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
index 81028c1608ea..e0ffdeecd99c 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Phrase;
/**
* Specialized sound model for key phrase detection.
@@ -24,6 +24,8 @@ import android.media.soundtrigger_middleware.Phrase;
* information indicated by phrases field.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseSoundModel {
/** Common part of sound model descriptor */
SoundModel common;
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger/Properties.aidl
index 9c56e7b98b3f..efa1b6aac68d 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/aidl/android/media/soundtrigger/Properties.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Capabilities of a sound trigger module.
* {@hide}
*/
-parcelable SoundTriggerModuleProperties {
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable Properties {
/** Implementor name */
String implementor;
/** Implementation description */
@@ -44,7 +46,7 @@ parcelable SoundTriggerModuleProperties {
int maxKeyPhrases;
/** Maximum number of concurrent users detected */
int maxUsers;
- /** All supported modes. e.g RecognitionMode.VOICE_TRIGGER */
+ /** All supported modes. Bitfield, indexed by RecognitionMode. */
int recognitionModes;
/** Supports seamless transition from detection to capture */
boolean captureTransition;
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
index 5c0eeb1e32b1..a00f0e58e293 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseRecognitionExtra;
/**
* Configuration for tuning behavior of an active recognition process.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable RecognitionConfig {
/* Capture and buffer audio for this recognition instance. */
boolean captureRequested;
@@ -34,6 +36,6 @@ parcelable RecognitionConfig {
*/
int audioCapabilities;
- /** Opaque capture configuration data. */
+ /** Capture configuration data. Content is implementation-defined. */
byte[] data;
}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
index a237ec1aa3b3..94668a30606d 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
@@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModelType;
/**
* An event that gets sent to indicate a recognition (or aborting of the recognition process).
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable RecognitionEvent {
/** Recognition status. */
- RecognitionStatus status;
+ RecognitionStatus status = RecognitionStatus.INVALID;
/** Event type, same as sound model type. */
- SoundModelType type;
+ SoundModelType type = SoundModelType.INVALID;
/** Is it possible to capture audio from this utterance buffered by the implementation. */
boolean captureAvailable;
- /* Audio session ID. framework use. */
- int captureSession;
/**
* Delay in ms between end of model detection and start of audio available for capture.
* A negative value is possible (e.g. if key phrase is also available for Capture.
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
index d8bfff4bec6f..ce2cffedda32 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Recognition mode.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum RecognitionMode {
/** Simple voice trigger. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
index d563edca547d..cccf0f3b6d49 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
@@ -13,14 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* A status for indicating the type of a recognition event.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum RecognitionStatus {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Recognition success. */
SUCCESS = 0,
/** Recognition aborted (e.g. capture preempted by another use-case. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger/SoundModel.aidl
index 8186fb741b59..94244d0ba0ff 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger/SoundModel.aidl
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger.SoundModelType;
import android.os.ParcelFileDescriptor;
/**
@@ -23,9 +23,11 @@ import android.os.ParcelFileDescriptor;
* aggregation.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable SoundModel {
/** Model type. */
- SoundModelType type;
+ SoundModelType type = SoundModelType.INVALID;
/** Unique sound model ID. */
String uuid;
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
index f2abc9af7780..34a9376a26b9 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
+++ b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
@@ -13,16 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Sound model type.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum SoundModelType {
- /** Unspecified sound model type */
- UNKNOWN = -1,
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Key phrase sound models */
KEYPHRASE = 0,
/** All models other than keyphrase */
diff --git a/media/aidl/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger/Status.aidl
index c7623f5bf491..ca1487fb119f 100644
--- a/media/aidl/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/aidl/android/media/soundtrigger/Status.aidl
@@ -13,13 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum Status {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Success. */
SUCCESS = 0,
/** Failure due to resource contention. This is typically a temporary condition. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 726af7681979..6092ac53ec39 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -15,8 +15,8 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionEvent;
/**
* Main interface for a client to get notifications of events coming from this module.
@@ -28,22 +28,29 @@ oneway interface ISoundTriggerCallback {
* Invoked whenever a recognition event is triggered (typically, on recognition, but also in
* case of external aborting of a recognition or a forced recognition event - see the status
* code in the event for determining).
+ * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+ * callback.
*/
- void onRecognition(int modelHandle, in RecognitionEvent event);
+ void onRecognition(int modelHandle, in RecognitionEvent event, int captureSession);
/**
* Invoked whenever a phrase recognition event is triggered (typically, on recognition, but
* also in case of external aborting of a recognition or a forced recognition event - see the
* status code in the event for determining).
+ * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+ * callback.
*/
- void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event);
+ void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event, int captureSession);
/**
- * Notifies the client the recognition has become available after previously having been
- * unavailable, or vice versa. This method will always be invoked once immediately after
- * attachment, and then every time there is a change in availability.
- * When availability changes from available to unavailable, all active recognitions are aborted,
- * and this event will be sent in addition to the abort event.
+ * Notifies the client that some start/load operations that have previously failed for resource
+ * reasons (threw a ServiceSpecificException(RESOURCE_CONTENTION) or have been preempted) may
+ * now succeed. This is not a guarantee, but a hint for the client to retry.
*/
- void onRecognitionAvailabilityChange(boolean available);
+ void onResourcesAvailable();
+ /**
+ * Notifies the client that a model had been preemptively unloaded by the service.
+ * The caller may retry after the next onRecognitionAvailabilityChange() callback.
+ */
+ void onModelUnloaded(int modelHandle);
/**
* Notifies the client that the associated module has crashed and restarted. The module instance
* is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
index c4a57857dd3d..0b46fd4ef5f0 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
@@ -15,11 +15,11 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
/**
* A sound-trigger module.
@@ -75,6 +75,9 @@ interface ISoundTriggerModule {
* Once a recognition event is passed to the client, the recognition automatically become
* inactive, unless the event is of the RecognitionStatus.FORCED kind. Client can also shut down
* the recognition explicitly, via stopRecognition.
+ *
+ * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that
+ * resources required for starting the model are currently consumed by other clients.
*/
void startRecognition(int modelHandle, in RecognitionConfig config);
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
index 667135ff61b9..6c210bf7ccd4 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
@@ -15,17 +15,18 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.media.soundtrigger.Properties;
/**
* A descriptor of an available sound trigger module, containing the handle used to reference the
* module, as well its capabilities.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable SoundTriggerModuleDescriptor {
/** Module handle to be used for attaching to it. */
int handle;
/** Module capabilities. */
- SoundTriggerModuleProperties properties;
+ Properties properties;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
new file mode 100644
index 000000000000..c3af3bfdfde7
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+ REPRESENTATION_POSITION = 0,
+ REPRESENTATION_INDEX = 2,
+ NONE = 0,
+ INVALID = -1073741824,
+ OUT_FRONT_LEFT = 1,
+ OUT_FRONT_RIGHT = 2,
+ OUT_FRONT_CENTER = 4,
+ OUT_LOW_FREQUENCY = 8,
+ OUT_BACK_LEFT = 16,
+ OUT_BACK_RIGHT = 32,
+ OUT_FRONT_LEFT_OF_CENTER = 64,
+ OUT_FRONT_RIGHT_OF_CENTER = 128,
+ OUT_BACK_CENTER = 256,
+ OUT_SIDE_LEFT = 512,
+ OUT_SIDE_RIGHT = 1024,
+ OUT_TOP_CENTER = 2048,
+ OUT_TOP_FRONT_LEFT = 4096,
+ OUT_TOP_FRONT_CENTER = 8192,
+ OUT_TOP_FRONT_RIGHT = 16384,
+ OUT_TOP_BACK_LEFT = 32768,
+ OUT_TOP_BACK_CENTER = 65536,
+ OUT_TOP_BACK_RIGHT = 131072,
+ OUT_TOP_SIDE_LEFT = 262144,
+ OUT_TOP_SIDE_RIGHT = 524288,
+ OUT_HAPTIC_A = 536870912,
+ OUT_HAPTIC_B = 268435456,
+ OUT_MONO = 1,
+ OUT_STEREO = 3,
+ OUT_2POINT1 = 11,
+ OUT_2POINT0POINT2 = 786435,
+ OUT_2POINT1POINT2 = 786443,
+ OUT_3POINT0POINT2 = 786439,
+ OUT_3POINT1POINT2 = 786447,
+ OUT_QUAD = 51,
+ OUT_QUAD_BACK = 51,
+ OUT_QUAD_SIDE = 1539,
+ OUT_SURROUND = 263,
+ OUT_PENTA = 55,
+ OUT_5POINT1 = 63,
+ OUT_5POINT1_BACK = 63,
+ OUT_5POINT1_SIDE = 1551,
+ OUT_5POINT1POINT2 = 786495,
+ OUT_5POINT1POINT4 = 184383,
+ OUT_6POINT1 = 319,
+ OUT_7POINT1 = 1599,
+ OUT_7POINT1POINT2 = 788031,
+ OUT_7POINT1POINT4 = 185919,
+ OUT_MONO_HAPTIC_A = 536870913,
+ OUT_STEREO_HAPTIC_A = 536870915,
+ OUT_HAPTIC_AB = 805306368,
+ OUT_MONO_HAPTIC_AB = 805306369,
+ OUT_STEREO_HAPTIC_AB = 805306371,
+ IN_LEFT = 4,
+ IN_RIGHT = 8,
+ IN_FRONT = 16,
+ IN_BACK = 32,
+ IN_LEFT_PROCESSED = 64,
+ IN_RIGHT_PROCESSED = 128,
+ IN_FRONT_PROCESSED = 256,
+ IN_BACK_PROCESSED = 512,
+ IN_PRESSURE = 1024,
+ IN_X_AXIS = 2048,
+ IN_Y_AXIS = 4096,
+ IN_Z_AXIS = 8192,
+ IN_BACK_LEFT = 65536,
+ IN_BACK_RIGHT = 131072,
+ IN_CENTER = 262144,
+ IN_LOW_FREQUENCY = 1048576,
+ IN_TOP_LEFT = 2097152,
+ IN_TOP_RIGHT = 4194304,
+ IN_VOICE_UPLINK = 16384,
+ IN_VOICE_DNLINK = 32768,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
new file mode 100644
index 000000000000..c11eb76857d2
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioConfig {
+ int sampleRateHz;
+ int channelMask;
+ android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+ android.media.audio.common.AudioOffloadInfo offloadInfo;
+ long frameCount;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
new file mode 100644
index 000000000000..b7c86599a2c4
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+ INVALID = -1,
+ DEFAULT = 0,
+ PCM = 0,
+ MP3 = 16777216,
+ AMR_NB = 33554432,
+ AMR_WB = 50331648,
+ AAC = 67108864,
+ HE_AAC_V1 = 83886080,
+ HE_AAC_V2 = 100663296,
+ VORBIS = 117440512,
+ OPUS = 134217728,
+ AC3 = 150994944,
+ E_AC3 = 167772160,
+ DTS = 184549376,
+ DTS_HD = 201326592,
+ IEC61937 = 218103808,
+ DOLBY_TRUEHD = 234881024,
+ EVRC = 268435456,
+ EVRCB = 285212672,
+ EVRCWB = 301989888,
+ EVRCNW = 318767104,
+ AAC_ADIF = 335544320,
+ WMA = 352321536,
+ WMA_PRO = 369098752,
+ AMR_WB_PLUS = 385875968,
+ MP2 = 402653184,
+ QCELP = 419430400,
+ DSD = 436207616,
+ FLAC = 452984832,
+ ALAC = 469762048,
+ APE = 486539264,
+ AAC_ADTS = 503316480,
+ SBC = 520093696,
+ APTX = 536870912,
+ APTX_HD = 553648128,
+ AC4 = 570425344,
+ LDAC = 587202560,
+ MAT = 603979776,
+ AAC_LATM = 620756992,
+ CELT = 637534208,
+ APTX_ADAPTIVE = 654311424,
+ LHDC = 671088640,
+ LHDC_LL = 687865856,
+ APTX_TWSP = 704643072,
+ MAIN_MASK = -16777216,
+ SUB_MASK = 16777215,
+ PCM_SUB_16_BIT = 1,
+ PCM_SUB_8_BIT = 2,
+ PCM_SUB_32_BIT = 3,
+ PCM_SUB_8_24_BIT = 4,
+ PCM_SUB_FLOAT = 5,
+ PCM_SUB_24_BIT_PACKED = 6,
+ MP3_SUB_NONE = 0,
+ AMR_SUB_NONE = 0,
+ AAC_SUB_MAIN = 1,
+ AAC_SUB_LC = 2,
+ AAC_SUB_SSR = 4,
+ AAC_SUB_LTP = 8,
+ AAC_SUB_HE_V1 = 16,
+ AAC_SUB_SCALABLE = 32,
+ AAC_SUB_ERLC = 64,
+ AAC_SUB_LD = 128,
+ AAC_SUB_HE_V2 = 256,
+ AAC_SUB_ELD = 512,
+ AAC_SUB_XHE = 768,
+ VORBIS_SUB_NONE = 0,
+ E_AC3_SUB_JOC = 1,
+ MAT_SUB_1_0 = 1,
+ MAT_SUB_2_0 = 2,
+ MAT_SUB_2_1 = 3,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
new file mode 100644
index 000000000000..b5d889ea5d0b
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioOffloadInfo {
+ int sampleRateHz;
+ int channelMask;
+ android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+ android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
+ int bitRatePerSecond;
+ long durationMicroseconds;
+ boolean hasVideo;
+ boolean isStreaming;
+ int bitWidth;
+ int bufferSize;
+ android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
new file mode 100644
index 000000000000..915c66814aa8
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioStreamType {
+ INVALID = -2,
+ DEFAULT = -1,
+ MIN = 0,
+ VOICE_CALL = 0,
+ SYSTEM = 1,
+ RING = 2,
+ MUSIC = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ BLUETOOTH_SCO = 6,
+ ENFORCED_AUDIBLE = 7,
+ DTMF = 8,
+ TTS = 9,
+ ACCESSIBILITY = 10,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
new file mode 100644
index 000000000000..f5130a4d0231
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
@@ -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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioUsage {
+ INVALID = -1,
+ UNKNOWN = 0,
+ MEDIA = 1,
+ VOICE_COMMUNICATION = 2,
+ VOICE_COMMUNICATION_SIGNALLING = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ ASSISTANCE_ACCESSIBILITY = 11,
+ ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ ASSISTANCE_SONIFICATION = 13,
+ GAME = 14,
+ VIRTUAL_SOURCE = 15,
+ ASSISTANT = 16,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
new file mode 100644
index 000000000000..5d88305484d0
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioCapabilities {
+ ECHO_CANCELLATION = 1,
+ NOISE_SUPPRESSION = 2,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
new file mode 100644
index 000000000000..5127a110efc5
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ConfidenceLevel {
+ int userId;
+ int levelPercent;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
new file mode 100644
index 000000000000..aadbf80b4f96
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum ModelParameter {
+ INVALID = -1,
+ THRESHOLD_FACTOR = 0,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
new file mode 100644
index 000000000000..f29b7284a275
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ModelParameterRange {
+ int minInclusive;
+ int maxInclusive;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
new file mode 100644
index 000000000000..11029ba9d25f
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Phrase {
+ int id;
+ int recognitionModes;
+ int[] users;
+ String locale;
+ String text;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
new file mode 100644
index 000000000000..b75d1b83c72a
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionEvent {
+ android.media.soundtrigger.RecognitionEvent common;
+ android.media.soundtrigger.PhraseRecognitionExtra[] phraseExtras;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
new file mode 100644
index 000000000000..e417c69454a5
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionExtra {
+ int id;
+ int recognitionModes;
+ int confidenceLevel;
+ android.media.soundtrigger.ConfidenceLevel[] levels;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
new file mode 100644
index 000000000000..b4b3854d4926
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseSoundModel {
+ android.media.soundtrigger.SoundModel common;
+ android.media.soundtrigger.Phrase[] phrases;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
new file mode 100644
index 000000000000..068db52a2a6b
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Properties {
+ String implementor;
+ String description;
+ int version;
+ String uuid;
+ String supportedModelArch;
+ int maxSoundModels;
+ int maxKeyPhrases;
+ int maxUsers;
+ int recognitionModes;
+ boolean captureTransition;
+ int maxBufferMs;
+ boolean concurrentCapture;
+ boolean triggerInEvent;
+ int powerConsumptionMw;
+ int audioCapabilities;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
new file mode 100644
index 000000000000..63cd2cbbb797
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionConfig {
+ boolean captureRequested;
+ android.media.soundtrigger.PhraseRecognitionExtra[] phraseRecognitionExtras;
+ int audioCapabilities;
+ byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
new file mode 100644
index 000000000000..e6cfb6bfc3f6
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionEvent {
+ android.media.soundtrigger.RecognitionStatus status = android.media.soundtrigger.RecognitionStatus.INVALID;
+ android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+ boolean captureAvailable;
+ int captureDelayMs;
+ int capturePreambleMs;
+ boolean triggerInData;
+ @nullable android.media.audio.common.AudioConfig audioConfig;
+ byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
new file mode 100644
index 000000000000..588291084f4a
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionMode {
+ VOICE_TRIGGER = 1,
+ USER_IDENTIFICATION = 2,
+ USER_AUTHENTICATION = 4,
+ GENERIC_TRIGGER = 8,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
new file mode 100644
index 000000000000..7881c28c7ecf
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionStatus {
+ INVALID = -1,
+ SUCCESS = 0,
+ ABORTED = 1,
+ FAILURE = 2,
+ FORCED = 3,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
new file mode 100644
index 000000000000..fe382643f3d2
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable SoundModel {
+ android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+ String uuid;
+ String vendorUuid;
+ @nullable ParcelFileDescriptor data;
+ int dataSize;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
new file mode 100644
index 000000000000..ac7864170535
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum SoundModelType {
+ INVALID = -1,
+ KEYPHRASE = 0,
+ GENERIC = 1,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
new file mode 100644
index 000000000000..29f3167ac196
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Status {
+ INVALID = -1,
+ SUCCESS = 0,
+ RESOURCE_CONTENTION = 1,
+ OPERATION_NOT_SUPPORTED = 2,
+ TEMPORARY_PERMISSION_DENIED = 3,
+ DEAD_OBJECT = 4,
+ INTERNAL_ERROR = 5,
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a031b4cfc911..07444ead836e 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -334,7 +334,19 @@ public final class AudioAttributes implements Parcelable {
};
/**
+ * @hide
+ */
+ @TestApi
+ public static int[] getSdkUsages() {
+ return SDK_USAGES;
+ }
+
+ /**
* Flag defining a behavior where the audibility of the sound will be ensured by the system.
+ * To ensure sound audibility, the system only uses built-in speakers or wired headphones
+ * and specifically excludes wireless audio devices.
+ * <p>Note this flag should only be used for sounds subject to regulatory behaviors in some
+ * countries, such as for camera shutter sound, and not for routing behaviors.
*/
public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
/**
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 1644ec892c7e..a8199c4d028e 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -175,6 +175,28 @@ import java.util.Objects;
* <br>These masks are an ORed composite of individual channel masks. For example
* {@link #CHANNEL_OUT_STEREO} is composed of {@link #CHANNEL_OUT_FRONT_LEFT} and
* {@link #CHANNEL_OUT_FRONT_RIGHT}.
+ * <p>
+ * The following diagram represents the layout of the output channels, as seen from above
+ * the listener (in the center at the "lis" position, facing the front-center channel).
+ * <pre>
+ * TFL ----- TFC ----- TFR T is Top
+ * | \ | / |
+ * | FL --- FC --- FR | F is Front
+ * | |\ | /| |
+ * | | BFL-BFC-BFR | | BF is Bottom Front
+ * | | | |
+ * | FWL lis FWR | W is Wide
+ * | | | |
+ * TSL SL TC SR TSR S is Side
+ * | | | |
+ * | BL --- BC -- BR | B is Back
+ * | / \ |
+ * TBL ----- TBC ----- TBR C is Center, L/R is Left/Right
+ * </pre>
+ * All "T" (top) channels are above the listener, all "BF" (bottom-front) channels are below the
+ * listener, all others are in the listener's horizontal plane. When used in conjunction, LFE1 and
+ * LFE2 are below the listener, when used alone, LFE plane is undefined.
+ * See the channel definitions for the abbreviations
*
* <h5 id="channelIndexMask">Channel index masks</h5>
* Channel index masks are introduced in API {@link android.os.Build.VERSION_CODES#M}. They allow
@@ -417,43 +439,62 @@ public final class AudioFormat implements Parcelable {
// Output channel mask definitions below are translated to the native values defined in
// in /system/media/audio/include/system/audio.h in the JNI code of AudioTrack
+ /** Front left output channel (see FL in channel diagram) */
public static final int CHANNEL_OUT_FRONT_LEFT = 0x4;
+ /** Front right output channel (see FR in channel diagram) */
public static final int CHANNEL_OUT_FRONT_RIGHT = 0x8;
+ /** Front center output channel (see FC in channel diagram) */
public static final int CHANNEL_OUT_FRONT_CENTER = 0x10;
+ /** LFE "low frequency effect" channel
+ * When used in conjunction with {@link #CHANNEL_OUT_LOW_FREQUENCY_2}, it is intended
+ * to contain the left low-frequency effect signal, also referred to as "LFE1"
+ * in ITU-R BS.2159-8 */
public static final int CHANNEL_OUT_LOW_FREQUENCY = 0x20;
+ /** Back left output channel (see BL in channel diagram) */
public static final int CHANNEL_OUT_BACK_LEFT = 0x40;
+ /** Back right output channel (see BR in channel diagram) */
public static final int CHANNEL_OUT_BACK_RIGHT = 0x80;
public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
+ /** Back center output channel (see BC in channel diagram) */
public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
+ /** Side left output channel (see SL in channel diagram) */
public static final int CHANNEL_OUT_SIDE_LEFT = 0x800;
+ /** Side right output channel (see SR in channel diagram) */
public static final int CHANNEL_OUT_SIDE_RIGHT = 0x1000;
- /** @hide */
+ /** Top center (above listener) output channel (see TC in channel diagram) */
public static final int CHANNEL_OUT_TOP_CENTER = 0x2000;
- /** @hide */
+ /** Top front left output channel (see TFL in channel diagram above FL) */
public static final int CHANNEL_OUT_TOP_FRONT_LEFT = 0x4000;
- /** @hide */
+ /** Top front center output channel (see TFC in channel diagram above FC) */
public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 0x8000;
- /** @hide */
+ /** Top front right output channel (see TFR in channel diagram above FR) */
public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 0x10000;
- /** @hide */
+ /** Top back left output channel (see TBL in channel diagram above BL) */
public static final int CHANNEL_OUT_TOP_BACK_LEFT = 0x20000;
- /** @hide */
+ /** Top back center output channel (see TBC in channel diagram above BC) */
public static final int CHANNEL_OUT_TOP_BACK_CENTER = 0x40000;
- /** @hide */
+ /** Top back right output channel (see TBR in channel diagram above BR) */
public static final int CHANNEL_OUT_TOP_BACK_RIGHT = 0x80000;
- /** @hide */
+ /** Top side left output channel (see TSL in channel diagram above SL) */
public static final int CHANNEL_OUT_TOP_SIDE_LEFT = 0x100000;
- /** @hide */
+ /** Top side right output channel (see TSR in channel diagram above SR) */
public static final int CHANNEL_OUT_TOP_SIDE_RIGHT = 0x200000;
- /** @hide */
+ /** Bottom front left output channel (see BFL in channel diagram below FL) */
public static final int CHANNEL_OUT_BOTTOM_FRONT_LEFT = 0x400000;
- /** @hide */
+ /** Bottom front center output channel (see BFC in channel diagram below FC) */
public static final int CHANNEL_OUT_BOTTOM_FRONT_CENTER = 0x800000;
- /** @hide */
+ /** Bottom front right output channel (see BFR in channel diagram below FR) */
public static final int CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 0x1000000;
- /** @hide */
+ /** The second LFE channel
+ * When used in conjunction with {@link #CHANNEL_OUT_LOW_FREQUENCY}, it is intended
+ * to contain the right low-frequency effect signal, also referred to as "LFE2"
+ * in ITU-R BS.2159-8 */
public static final int CHANNEL_OUT_LOW_FREQUENCY_2 = 0x2000000;
+ /** Front wide left output channel (see FWL in channel diagram) */
+ public static final int CHANNEL_OUT_FRONT_WIDE_LEFT = 0x4000000;
+ /** Front wide right output channel (see FWR in channel diagram) */
+ public static final int CHANNEL_OUT_FRONT_WIDE_RIGHT = 0x8000000;
public static final int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT;
public static final int CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT);
@@ -466,6 +507,7 @@ public final class AudioFormat implements Parcelable {
public static final int CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER);
// aka 5POINT1_BACK
+ /** Output channel mask for 5.1 */
public static final int CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
/** @hide */
@@ -477,26 +519,39 @@ public final class AudioFormat implements Parcelable {
@Deprecated public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER);
+ /** Output channel mask for 7.1 */
// matches AUDIO_CHANNEL_OUT_7POINT1
public static final int CHANNEL_OUT_7POINT1_SURROUND = (
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT |
CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
CHANNEL_OUT_LOW_FREQUENCY);
- /** @hide */
+ /** Output channel mask for 5.1.2
+ * Same as 5.1 with the addition of left and right top channels */
public static final int CHANNEL_OUT_5POINT1POINT2 = (CHANNEL_OUT_5POINT1 |
CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
- /** @hide */
+ /** Output channel mask for 5.1.4
+ * Same as 5.1 with the addition of four top channels */
public static final int CHANNEL_OUT_5POINT1POINT4 = (CHANNEL_OUT_5POINT1 |
CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_RIGHT |
CHANNEL_OUT_TOP_BACK_LEFT | CHANNEL_OUT_TOP_BACK_RIGHT);
- /** @hide */
+ /** Output channel mask for 7.1.2
+ * Same as 7.1 with the addition of left and right top channels*/
public static final int CHANNEL_OUT_7POINT1POINT2 = (CHANNEL_OUT_7POINT1_SURROUND |
CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
- /** @hide */
+ /** Output channel mask for 7.1.4
+ * Same as 7.1 with the addition of four top channels */
public static final int CHANNEL_OUT_7POINT1POINT4 = (CHANNEL_OUT_7POINT1_SURROUND |
CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_RIGHT |
CHANNEL_OUT_TOP_BACK_LEFT | CHANNEL_OUT_TOP_BACK_RIGHT);
+ /** Output channel mask for 9.1.4
+ * Same as 7.1.4 with the addition of left and right front wide channels */
+ public static final int CHANNEL_OUT_9POINT1POINT4 = (CHANNEL_OUT_7POINT1POINT4
+ | CHANNEL_OUT_FRONT_WIDE_LEFT | CHANNEL_OUT_FRONT_WIDE_RIGHT);
+ /** Output channel mask for 9.1.6
+ * Same as 9.1.4 with the addition of left and right top side channels */
+ public static final int CHANNEL_OUT_9POINT1POINT6 = (CHANNEL_OUT_9POINT1POINT4
+ | CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
/** @hide */
public static final int CHANNEL_OUT_13POINT_360RA = (
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bd3ca5a80f96..1a569152d9ac 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -390,6 +390,18 @@ public class AudioManager {
*/
@Deprecated public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
+ /** @hide */
+ private static final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+ AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+ AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
+
+ /** @hide */
+ @TestApi
+ public static final int[] getPublicStreamTypes() {
+ return PUBLIC_STREAM_TYPES;
+ }
+
/**
* Increase the ringer volume.
*
@@ -922,8 +934,8 @@ public class AudioManager {
public void adjustStreamVolume(int streamType, int direction, int flags) {
final IAudioService service = getService();
try {
- service.adjustStreamVolume(streamType, direction, flags,
- getContext().getOpPackageName());
+ service.adjustStreamVolumeWithAttribution(streamType, direction, flags,
+ getContext().getOpPackageName(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -993,7 +1005,7 @@ public class AudioManager {
final IAudioService service = getService();
try {
service.setMasterMute(mute, flags, getContext().getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1075,6 +1087,7 @@ public class AudioManager {
* @return The minimum valid volume index for the stream.
* @see #getStreamVolume(int)
*/
+ @TestApi
public int getStreamMinVolumeInt(int streamType) {
final IAudioService service = getService();
try {
@@ -1245,7 +1258,8 @@ public class AudioManager {
public void setStreamVolume(int streamType, int index, int flags) {
final IAudioService service = getService();
try {
- service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
+ service.setStreamVolumeWithAttribution(streamType, index, flags,
+ getContext().getOpPackageName(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1270,7 +1284,7 @@ public class AudioManager {
final IAudioService service = getService();
try {
service.setVolumeIndexForAttributes(attr, index, flags,
- getContext().getOpPackageName());
+ getContext().getOpPackageName(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2730,7 +2744,7 @@ public class AudioManager {
final IAudioService service = getService();
try {
service.setMicrophoneMute(on, getContext().getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4257,7 +4271,9 @@ public class AudioManager {
afr.getFocusGain(), mICallBack,
mAudioFocusDispatcher,
clientId,
- getContext().getOpPackageName() /* package name */, afr.getFlags(),
+ getContext().getOpPackageName() /* package name */,
+ getContext().getAttributionTag(),
+ afr.getFlags(),
ap != null ? ap.cb() : null,
sdk);
} catch (RemoteException e) {
@@ -4362,6 +4378,7 @@ public class AudioManager {
durationHint, mICallBack, null,
AudioSystem.IN_VOICE_COMM_FOCUS_ID,
getContext().getOpPackageName(),
+ getContext().getAttributionTag(),
AUDIOFOCUS_FLAG_LOCK,
null /* policy token */, 0 /* sdk n/a here*/);
} catch (RemoteException e) {
@@ -7062,14 +7079,11 @@ public class AudioManager {
@TestApi
@NonNull
public Map<Integer, Boolean> getSurroundFormats() {
- Map<Integer, Boolean> surroundFormats = new HashMap<>();
- int status = AudioSystem.getSurroundFormats(surroundFormats);
- if (status != AudioManager.SUCCESS) {
- // fail and bail!
- Log.e(TAG, "getSurroundFormats failed:" + status);
- return new HashMap<Integer, Boolean>(); // Always return a map.
+ try {
+ return getService().getSurroundFormats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return surroundFormats;
}
/**
@@ -7116,15 +7130,14 @@ public class AudioManager {
*
* @return a list of surround formats
*/
- public ArrayList<Integer> getReportedSurroundFormats() {
- ArrayList<Integer> reportedSurroundFormats = new ArrayList<>();
- int status = AudioSystem.getReportedSurroundFormats(reportedSurroundFormats);
- if (status != AudioManager.SUCCESS) {
- // fail and bail!
- Log.e(TAG, "getReportedSurroundFormats failed:" + status);
- return new ArrayList<Integer>(); // Always return a list.
+ @TestApi
+ @NonNull
+ public List<Integer> getReportedSurroundFormats() {
+ try {
+ return getService().getReportedSurroundFormats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return reportedSurroundFormats;
}
/**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8012f03d84b3..321db4e2f050 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2051,7 +2051,8 @@ public class AudioSystem
};
/** @hide */
- public static String streamToString(int stream) {
+ @TestApi
+ public static @NonNull String streamToString(int stream) {
if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
return "UNKNOWN_STREAM_" + stream;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 23d9532e11a0..8822aeac80e4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2082,8 +2082,9 @@ public class AudioTrack extends PlayerBase
* It may also be adjusted slightly for internal reasons.
* If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
* will be returned.
- * <p>This method is only supported for PCM audio.
- * It is not supported for compressed audio tracks.
+ * <p>This method is supported for PCM audio at all API levels.
+ * Compressed audio is supported in API levels 33 and above.
+ * For compressed streams the size of a frame is considered to be exactly one byte.
*
* @param bufferSizeInFrames requested buffer size in frames
* @return the actual buffer size in frames or an error code,
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c08c368af55a..b0c4a3ba6b48 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -77,15 +77,17 @@ interface IAudioService {
oneway void playerSessionId(in int piid, in int sessionId);
// Java-only methods below.
-
- oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
- String callingPackage, String caller);
-
void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
+ void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
+ in String callingPackage, in String attributionTag);
+
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void setStreamVolume(int streamType, int index, int flags, String callingPackage);
+ void setStreamVolumeWithAttribution(int streamType, int index, int flags,
+ in String callingPackage, in String attributionTag);
+
oneway void handleVolumeKey(in KeyEvent event, boolean isOnTv,
String callingPackage, String caller);
@@ -95,7 +97,8 @@ interface IAudioService {
boolean isMasterMute();
- void setMasterMute(boolean mute, int flags, String callingPackage, int userId);
+ void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
+ in String attributionTag);
@UnsupportedAppUsage
int getStreamVolume(int streamType);
@@ -107,7 +110,8 @@ interface IAudioService {
List<AudioVolumeGroup> getAudioVolumeGroups();
- void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags, String callingPackage);
+ void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags,
+ String callingPackage, in String attributionTag);
int getVolumeIndexForAttributes(in AudioAttributes aa);
@@ -125,7 +129,7 @@ interface IAudioService {
boolean isMicrophoneMuted();
- void setMicrophoneMute(boolean on, String callingPackage, int userId);
+ void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag);
oneway void setMicrophoneMuteFromSwitch(boolean on);
@@ -159,6 +163,10 @@ interface IAudioService {
oneway void reloadAudioSettings();
+ Map getSurroundFormats();
+
+ List getReportedSurroundFormats();
+
boolean setSurroundFormatEnabled(int audioFormat, boolean enabled);
boolean isSurroundFormatEnabled(int audioFormat);
@@ -182,8 +190,8 @@ interface IAudioService {
boolean isBluetoothA2dpOn();
int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
- IAudioPolicyCallback pcb, int sdk);
+ IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
+ in String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk);
int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa,
in String callingPackageName);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 1616c03112a8..bac44adbe101 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -399,7 +399,7 @@ public abstract class Image implements AutoCloseable {
* <p>The number and meaning of the planes in an Image are determined by the
* format of the Image.</p>
*
- * <p>Once the Image has been closed, any access to the the plane's
+ * <p>Once the Image has been closed, any access to the plane's
* ByteBuffer will fail.</p>
*
* @see #getFormat
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index cc05ecd3f18e..521cbcbda079 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1695,7 +1695,6 @@ final public class MediaCodec {
private static final int CB_ERROR = 3;
private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
-
private class EventHandler extends Handler {
private MediaCodec mCodec;
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
index a46095484fe7..5d7a8b312b9b 100644
--- a/media/java/android/media/MediaCodecList.java
+++ b/media/java/android/media/MediaCodecList.java
@@ -18,7 +18,6 @@ package android.media;
import android.util.Log;
-import android.media.MediaCodecInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
@@ -166,7 +165,8 @@ final public class MediaCodecList {
/**
* Create a list of media-codecs of a specific kind.
- * @param kind Either {@code REGULAR_CODECS} or {@code ALL_CODECS}.
+ * @param kind Either {@link MediaCodecList#REGULAR_CODECS} or
+ * {@link MediaCodecList#ALL_CODECS}.
*/
public MediaCodecList(int kind) {
initCodecList();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4f761ba0bf6a..ccd830a37d89 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@ package android.media;
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -4308,7 +4307,7 @@ public class MediaPlayer extends PlayerBase
@RequiresPermission(BIND_IMS_SERVICE)
public void setOnRtpRxNoticeListener(
@NonNull Context context,
- @NonNull @CallbackExecutor Executor executor,
+ @NonNull Executor executor,
@NonNull OnRtpRxNoticeListener listener) {
Objects.requireNonNull(context);
Preconditions.checkArgument(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 341bb8d5309a..f49e045b36b9 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -171,8 +171,12 @@ public class MediaRecorder implements AudioRouting,
}
/**
+ *
* Sets the {@link LogSessionId} for MediaRecorder.
*
+ * <p>The log session ID is a random 32-byte hexadecimal string that is used for monitoring the
+ * MediaRecorder performance.</p>
+ *
* @param id the global ID for monitoring the MediaRecorder performance
*/
public void setLogSessionId(@NonNull LogSessionId id) {
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 345d9b27c8a8..4de63f980ca5 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -2034,8 +2034,8 @@ public class MediaRouter {
public void requestSetVolume(int volume) {
if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
try {
- sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
- ActivityThread.currentPackageName());
+ sStatic.mAudioService.setStreamVolumeWithAttribution(mPlaybackStream, volume, 0,
+ ActivityThread.currentPackageName(), null);
} catch (RemoteException e) {
Log.e(TAG, "Error setting local stream volume", e);
}
@@ -2053,8 +2053,8 @@ public class MediaRouter {
try {
final int volume =
Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
- sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
- ActivityThread.currentPackageName());
+ sStatic.mAudioService.setStreamVolumeWithAttribution(mPlaybackStream, volume, 0,
+ ActivityThread.currentPackageName(), null);
} catch (RemoteException e) {
Log.e(TAG, "Error setting local stream volume", e);
}
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 0c733482b2b5..d00e5b55fd14 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -113,12 +113,10 @@ public class AudioMix {
*/
public static final int MIX_TYPE_INVALID = -1;
/**
- * @hide
* Mix type indicating playback streams are mixed.
*/
public static final int MIX_TYPE_PLAYERS = 0;
/**
- * @hide
* Mix type indicating recording streams are mixed.
*/
public static final int MIX_TYPE_RECORDERS = 1;
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index abbcc669504d..99ddff22f6a2 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -16,6 +16,9 @@
package android.media.audiopolicy;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +27,7 @@ import android.os.Build;
import android.os.Parcel;
import android.util.Log;
+import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
@@ -199,7 +203,19 @@ public class AudioMixingRule {
}
private final int mTargetMixType;
- int getTargetMixType() { return mTargetMixType; }
+
+ /** @hide */
+ @IntDef({AudioMix.MIX_TYPE_PLAYERS, AudioMix.MIX_TYPE_RECORDERS})
+ @Retention(SOURCE)
+ public @interface MixType {}
+
+ /**
+ * Gets target mix type of the mixing rule.
+ */
+ public @MixType int getTargetMixType() {
+ return mTargetMixType;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<AudioMixMatchCriterion> mCriteria;
/** @hide */
@@ -469,17 +485,24 @@ public class AudioMixingRule {
}
/**
- * Set target mix type of the mixing rule.
+ * Sets target mix type of the mixing rule.
*
- * <p>Note: If the mix type was not specified, it will be decided automatically by mixing
- * rule. For {@link #RULE_MATCH_UID}, the default type is {@link AudioMix#MIX_TYPE_PLAYERS}.
+ * <p>Note: If the mix type was not specified, it will be decided automatically by matched
+ * mixing rule. For example, {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or {@link
+ * AudioMixingRule#RULE_MATCH_USERID} applied {@link AudioMix#MIX_TYPE_PLAYERS}, {@link
+ * AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} applied {@link
+ * AudioMix#MIX_TYPE_RECORDERS}. For {@link AudioMixingRule#RULE_MATCH_UID}, the mix type
+ * could be {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}, and
+ * {@link AudioMix#MIX_TYPE_PLAYERS} is the default value.
*
* @param mixType {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}
* @return the same Builder instance.
- *
- * @hide
*/
- public @NonNull Builder setTargetMixType(int mixType) {
+ public @NonNull Builder setTargetMixType(@MixType int mixType) {
+ if (mixType != AudioMix.MIX_TYPE_PLAYERS && mixType != AudioMix.MIX_TYPE_RECORDERS) {
+ throw new IllegalArgumentException("Illegal argument for mix type");
+ }
+
mTargetMixType = mixType;
Log.i("AudioMixingRule", "Builder setTargetMixType " + mixType);
return this;
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index fca3498838ad..31d596765bcc 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -19,6 +19,7 @@ package android.media.audiopolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.media.MediaRecorder;
@@ -240,6 +241,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return the legacy stream type relevant for the given {@link AudioAttributes}.
* If none is found, it return DEFAULT stream type.
*/
+ @TestApi
public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -273,6 +275,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return the volume group id relevant for the given streamType.
* If none is found, {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} is returned.
*/
+ @TestApi
public int getVolumeGroupIdForLegacyStreamType(int streamType) {
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
if (aag.supportsStreamType(streamType)) {
@@ -288,6 +291,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return the volume group id associated with the given audio attributes if found,
* {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} otherwise.
*/
+ @TestApi
public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -352,11 +356,19 @@ public final class AudioProductStrategy implements Parcelable {
* @hide
* Default attributes, with default source to be aligned with native.
*/
- public static final @NonNull AudioAttributes sDefaultAttributes =
+ private static final @NonNull AudioAttributes DEFAULT_ATTRIBUTES =
new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT)
.build();
/**
+ * @hide
+ */
+ @TestApi
+ public static @NonNull AudioAttributes getDefaultAttributes() {
+ return DEFAULT_ATTRIBUTES;
+ }
+
+ /**
* To avoid duplicating the logic in java and native, we shall make use of
* native API native_get_product_strategies_from_audio_attributes
* Keep in sync with frameworks/av/media/libaudioclient/AudioProductStrategy::attributesMatches
@@ -369,7 +381,7 @@ public final class AudioProductStrategy implements Parcelable {
Preconditions.checkNotNull(attr, "attr must not be null");
String refFormattedTags = TextUtils.join(";", refAttr.getTags());
String cliFormattedTags = TextUtils.join(";", attr.getTags());
- if (refAttr.equals(sDefaultAttributes)) {
+ if (refAttr.equals(DEFAULT_ATTRIBUTES)) {
return false;
}
return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN)
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6b329f8ec694..b1baf94f844b 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -23,6 +23,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -39,6 +41,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.util.Xml;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -99,6 +102,7 @@ public class TvView extends ViewGroup {
private int mSurfaceWidth;
private int mSurfaceHeight;
private final AttributeSet mAttrs;
+ private final XmlResourceParser mParser;
private final int mDefStyleAttr;
private int mWindowZOrder;
private boolean mUseRequestedSurfaceLayout;
@@ -168,7 +172,16 @@ public class TvView extends ViewGroup {
public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mAttrs = attrs;
+ int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+ if (sourceResId != Resources.ID_NULL) {
+ Log.d(TAG, "Build local AttributeSet");
+ mParser = context.getResources().getXml(sourceResId);
+ mAttrs = Xml.asAttributeSet(mParser);
+ } else {
+ Log.d(TAG, "Use passed in AttributeSet");
+ mParser = null;
+ mAttrs = attrs;
+ }
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index ee70714ce763..e8ef46499dc8 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -46,6 +46,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -112,21 +113,34 @@ public abstract class MediaBrowserService extends Service {
/**
* All the info about a connection.
*/
- private class ConnectionRecord implements IBinder.DeathRecipient {
- String pkg;
- int uid;
- int pid;
- Bundle rootHints;
- IMediaBrowserServiceCallbacks callbacks;
- BrowserRoot root;
- HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+ private static class ConnectionRecord implements IBinder.DeathRecipient {
+ public final MediaBrowserService service;
+ public final String pkg;
+ public final int pid;
+ public final int uid;
+ public final Bundle rootHints;
+ public final IMediaBrowserServiceCallbacks callbacks;
+ public final BrowserRoot root;
+ public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+
+ ConnectionRecord(
+ MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
+ IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
+ this.service = service;
+ this.pkg = pkg;
+ this.pid = pid;
+ this.uid = uid;
+ this.rootHints = rootHints;
+ this.callbacks = callbacks;
+ this.root = root;
+ }
@Override
public void binderDied() {
- mHandler.post(new Runnable() {
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
- mConnections.remove(callbacks.asBinder());
+ service.mConnections.remove(callbacks.asBinder());
}
});
}
@@ -199,39 +213,46 @@ public abstract class MediaBrowserService extends Service {
}
}
- private class ServiceBinder extends IMediaBrowserService.Stub {
+ private static class ServiceBinder extends IMediaBrowserService.Stub {
+ private WeakReference<MediaBrowserService> mService;
+
+ private ServiceBinder(MediaBrowserService service) {
+ mService = new WeakReference(service);
+ }
+
@Override
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- if (!isValidPackage(pkg, uid)) {
+ if (!service.isValidPackage(pkg, uid)) {
throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+ " package=" + pkg);
}
- mHandler.post(new Runnable() {
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
- mConnections.remove(b);
-
- final ConnectionRecord connection = new ConnectionRecord();
- connection.pkg = pkg;
- connection.pid = pid;
- connection.uid = uid;
- connection.rootHints = rootHints;
- connection.callbacks = callbacks;
+ service.mConnections.remove(b);
- mCurConnection = connection;
- connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
- mCurConnection = null;
+ // Temporarily sets a placeholder ConnectionRecord to make
+ // getCurrentBrowserInfo() work in onGetRoot().
+ service.mCurConnection =
+ new ConnectionRecord(
+ service, pkg, pid, uid, rootHints, callbacks, null);
+ BrowserRoot root = service.onGetRoot(pkg, uid, rootHints);
+ service.mCurConnection = null;
// If they didn't return something, don't allow this client.
- if (connection.root == null) {
+ if (root == null) {
Log.i(TAG, "No root for client " + pkg + " from service "
+ getClass().getName());
try {
@@ -242,16 +263,19 @@ public abstract class MediaBrowserService extends Service {
}
} else {
try {
- mConnections.put(b, connection);
+ ConnectionRecord connection =
+ new ConnectionRecord(
+ service, pkg, pid, uid, rootHints, callbacks, root);
+ service.mConnections.put(b, connection);
b.linkToDeath(connection, 0);
- if (mSession != null) {
+ if (service.mSession != null) {
callbacks.onConnect(connection.root.getRootId(),
- mSession, connection.root.getExtras());
+ service.mSession, connection.root.getExtras());
}
} catch (RemoteException ex) {
Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+ "pkg=" + pkg);
- mConnections.remove(b);
+ service.mConnections.remove(b);
}
}
}
@@ -260,13 +284,18 @@ public abstract class MediaBrowserService extends Service {
@Override
public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
- final ConnectionRecord old = mConnections.remove(b);
+ final ConnectionRecord old = service.mConnections.remove(b);
if (old != null) {
// TODO
old.callbacks.asBinder().unlinkToDeath(old, 0);
@@ -283,20 +312,25 @@ public abstract class MediaBrowserService extends Service {
@Override
public void addSubscription(final String id, final IBinder token, final Bundle options,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Get the record for the connection
- final ConnectionRecord connection = mConnections.get(b);
+ final ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "addSubscription for callback that isn't registered id="
+ id);
return;
}
- MediaBrowserService.this.addSubscription(id, connection, token, options);
+ service.addSubscription(id, connection, token, options);
}
});
}
@@ -310,18 +344,23 @@ public abstract class MediaBrowserService extends Service {
@Override
public void removeSubscription(final String id, final IBinder token,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
+ ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "removeSubscription for callback that isn't registered id="
+ id);
return;
}
- if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
+ if (!service.removeSubscription(id, connection, token)) {
Log.w(TAG, "removeSubscription called for " + id
+ " which is not subscribed");
}
@@ -332,16 +371,21 @@ public abstract class MediaBrowserService extends Service {
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
+ ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
return;
}
- performLoadItem(mediaId, connection, receiver);
+ service.performLoadItem(mediaId, connection, receiver);
}
});
}
@@ -350,7 +394,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void onCreate() {
super.onCreate();
- mBinder = new ServiceBinder();
+ mBinder = new ServiceBinder(this);
}
@Override
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 7c5f58e31108..116237f6b998 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -282,7 +282,6 @@ status_t JMediaExtractor::getMetrics(Parcel *reply) const {
return status;
}
-
status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
return mImpl->getSampleMeta(sampleMeta);
}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
index 7919723b80a8..a00031a28b4f 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -20,16 +20,15 @@ import android.filterfw.core.Filter;
import android.filterfw.core.FilterContext;
import android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GLFrame;
import android.filterfw.core.GenerateFieldPort;
import android.filterfw.core.GenerateFinalPort;
-import android.filterfw.core.GLFrame;
import android.filterfw.core.MutableFrameFormat;
import android.filterfw.core.ShaderProgram;
import android.filterfw.format.ImageFormat;
import android.graphics.SurfaceTexture;
-import android.os.ConditionVariable;
import android.opengl.Matrix;
-
+import android.os.ConditionVariable;
import android.util.Log;
/** <p>A filter that converts textures from a SurfaceTexture object into frames for
@@ -57,7 +56,7 @@ public class SurfaceTextureSource extends Filter {
public void onSurfaceTextureSourceReady(SurfaceTexture source);
}
/** A callback to send the internal SurfaceTexture object to, once it is
- * created. This callback will be called when the the filter graph is
+ * created. This callback will be called when the filter graph is
* preparing to execute, but before any processing has actually taken
* place. The SurfaceTexture object passed to this callback is the only way
* to feed this filter. When the filter graph is shutting down, this
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index 1131c623e428..27cf9434b34d 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -255,7 +255,7 @@ public class AudioManagerTest extends AudioVolumesTestBase {
AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver);
- final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES);
+ final List<Integer> publicStreams = Ints.asList(AudioManager.getPublicStreamTypes());
try {
// Validate Audio Volume Groups callback reception
for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
index c0f596b974e1..0e918d13e042 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
@@ -71,7 +72,7 @@ public class AudioProductStrategyTest extends AudioVolumesTestBase {
assertNotNull(audioProductStrategies);
assertTrue(audioProductStrategies.size() > 0);
- for (final int streamType : PUBLIC_STREAM_TYPES) {
+ for (final int streamType : AudioManager.getPublicStreamTypes()) {
AudioAttributes aaFromStreamType =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
streamType);
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
index a17d65cf7376..b30ef307855d 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
@@ -37,15 +37,10 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
// Default matches the invalid (empty) attributes from native.
// The difference is the input source default which is not aligned between native and java
public static final AudioAttributes sDefaultAttributes =
- AudioProductStrategy.sDefaultAttributes;
+ AudioProductStrategy.getDefaultAttributes();
public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build();
- public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
- AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
- AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
- AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
-
public AudioVolumesTestBase() {
super("com.android.audiopolicytest", AudioPolicyTest.class);
}
@@ -63,7 +58,7 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
}
AudioAttributes avgAttributes = sDefaultAttributes;
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
avgAttributes = aa;
break;
}
@@ -89,7 +84,7 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
assertTrue(!avg.getAudioAttributes().isEmpty());
AudioAttributes avgAttributes = sDefaultAttributes;
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
avgAttributes = aa;
break;
}
@@ -114,7 +109,7 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
// Store the original volumes that that they can be recovered in tearDown().
mOriginalStreamVolumes.clear();
- for (int streamType : PUBLIC_STREAM_TYPES) {
+ for (int streamType : AudioManager.getPublicStreamTypes()) {
mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
}
// Store the original volume per attributes so that they can be recovered in tearDown()
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index cdf4851a3e3d..3faed5525946 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Stel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</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="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 a03ea0dfcd4c..99466d7d2e1a 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; እንዲያስተዳድር ያቀናብሩት"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string>
- <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</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="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 970c46bd6133..c3f1e73d3ad4 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏اضبط &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"يجب توفّر تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"نعم"</string>
- <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</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="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 477844c2a477..2a2bc25c7881 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ছেট কৰক - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হয়"</string>
- <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</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="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 f10c639a0368..2ec13c587166 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tərəfindən idarə olunmasını ayarlayın - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME">%1$s</xliff:g> tələb olunur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string>
- <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</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="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 e8542f364027..d687b05896c6 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</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="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 13be6f245ac0..2236052f5545 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
<string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</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="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 3bda5e6104c8..996ca9062f80 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Задайте &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"За управление на <xliff:g id="PROFILE_NAME">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</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="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 d3bc5152af9a..16d25ce57870 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</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="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"না থাক"</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="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 905b3061ffc6..10f753c1cf45 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Potrebna je aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</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="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 86dc6940abe2..e55a033cf800 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
<string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defineix que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> és necessària per gestionar el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</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="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 389ccd0ccc95..48fbda10cfed 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ke správě profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ano"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, díky"</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="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 5a31f9bba4b6..446c301747df 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -16,12 +16,12 @@
<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 enhedshåndtering"</string>
+ <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</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>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angiv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tak"</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="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 b643eb2936ce..33d831d41aec 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) zu verwalten. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nein danke"</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="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 60de2ffe0572..7a78c0669825 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string>
- <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</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="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 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</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="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 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</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="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 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</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="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 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</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="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 f3c4b1dcb434..6cc56a4f44e7 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
<string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎Set &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ - &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
- <string name="profile_summary" msgid="2009764182871566255">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="consent_yes" msgid="4055438216605487056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎Yes‎‏‎‎‏‎"</string>
- <string name="consent_no" msgid="1335543792857823917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎No thanks‎‏‎‎‏‎"</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="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 4fbb57ed9440..dc1315916159 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se requiere <xliff:g id="APP_NAME">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</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="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 5ca9305ce4d6..7e37c1d33522 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Haz que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se necesita <xliff:g id="APP_NAME">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</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="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 357f05237b85..399556de789b 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
<string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME">%2$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jah"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</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="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 14c7154acdd3..764505e52959 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kudea dezan"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kudeatzeko. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bai"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ez"</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="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 6bb9620f09f7..07d04aa1ecc9 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏تنظیم &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‏&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> به <xliff:g id="APP_NAME">%1$s</xliff:g> نیاز دارید. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"بله"</string>
- <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</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="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 5a9c1cd66aa8..528d16c2e425 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
<string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Aseta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnointiin. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</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="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 b31babda1a00..2cf872cc7c71 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Utiliser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non merci"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour 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="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 08c93a2c31c6..ba2fc8ea9424 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Définir &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, merci"</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="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 c95b90e73edc..5f9a8d786739 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Necesítase a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Si"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</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="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 7e4104208446..71cf012f24b3 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
- <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</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="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 ac95cc620bfa..d4dd1cb4f1b1 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"आपके <xliff:g id="PROFILE_NAME">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"हां"</string>
- <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</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="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 df8451f5f127..87c5ae2670e9 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> potrebna je za upravljanje vašim <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</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="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 ff1c6c54d7ba..c7ceb384ed16 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás beállítása a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kezelésére"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Szükség van a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kezeléséhez. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Igen"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nem"</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="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 194223d1d416..26f7990d7b66 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ձեր <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Այո"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</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="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 58bf3cb4bca4..b0618d409527 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Perlu <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak"</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="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 cc5b98939b71..b7d7c6abf749 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
<string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Já"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</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="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 4cbefd801187..ce003e7d7645 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"È richiesta l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sì"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, grazie"</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="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 8663e56ecf14..54c523c92f4b 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,11 +17,11 @@
<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="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="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="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
<string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
- <string name="consent_no" msgid="1335543792857823917">"לא תודה"</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="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 ca17336bfb23..f92fafe54fde 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; を管理するよう設定する"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME">%1$s</xliff:g> が必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"はい"</string>
- <string name="consent_no" msgid="1335543792857823917">"いいえ"</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="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 300c94f63cbe..34efdd2223ac 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
<string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string>
- <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</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="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 94d6c3ed2d5f..3c7f697b984f 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Иә"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</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="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 db13fe7884af..74ccd84956ea 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string>
- <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</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="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 0225166849fd..2a82d1fd6bac 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
- <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</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="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 1363e57e39a3..6ca01bff5364 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
<string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)을(를) 관리하도록 설정"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> 프로필을 관리하려면 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"예"</string>
- <string name="consent_no" msgid="1335543792857823917">"취소"</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="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 c01e2350aa04..18d38a832e0b 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
<string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарсын"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME">%1$s</xliff:g> керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</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="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 68218dd79c50..a1eb71342446 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</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="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 5fd8280affca..65f371d3d312 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</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>
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; būtų valdomas programos &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Norint valdyti jūsų <xliff:g id="PROFILE_NAME">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Taip"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</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="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 bf036ec70d73..b18bfe4a4243 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
<string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) pārvaldībai"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jā"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</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="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 427ca8f940a0..9d745c413b39 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"уред"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, фала"</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="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 a48c45f73ae7..28c88da9e4af 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
<string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; സജ്ജീകരിക്കുക - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string>
- <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</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="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 7ac20e613185..11e61d910ee4 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
<string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г тохируулна уу - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Таны <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string>
- <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</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="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 144698b9bc4e..c73ed283c768 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</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>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
- <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</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="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 7bea2c91fd2d..d2aebb45f49e 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
<string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; anda"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</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="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 9c2783cdcbbb..45c9d8b240fa 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
<string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</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="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 26fbb0350edc..af1ffe94cd36 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei takk"</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="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 f289b3780672..b29f94ce1b05 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -16,12 +16,12 @@
<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="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</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>
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
<string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"अँ"</string>
- <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</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="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 0c9cdffd4e17..a56fb9a62ce7 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; instellen om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te beheren"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Je hebt <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, bedankt"</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="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 c8c680f8f3a9..8e43213a9e43 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</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="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 0da94105a576..54f4f8c42302 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</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="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 b07af57a936a..a989baa4e36c 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ustaw aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> na urządzeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Tak"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, dziękuję"</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="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 16906f62f9f1..d2724c0a8127 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</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="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 745d1630dd86..2f5a53b2e2a7 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Não, obrigado"</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="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 16906f62f9f1..d2724c0a8127 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</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="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 187cfbdfe6f0..4df74de2a441 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</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="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 8dd9a392712b..ea372d51658b 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Нет"</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="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 9e7c02e0c0d9..a5c2c8842830 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට <xliff:g id="APP_NAME">%1$s</xliff:g> අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string>
- <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</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="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 55a47c2df427..a9bf77f809e5 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Na správu profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Áno"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</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="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 159afd543609..4eb8f5029fcd 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Za upravljanje naprave <xliff:g id="PROFILE_NAME">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</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="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 6fa759c15905..34357b478a17 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Nevojitet <xliff:g id="APP_NAME">%1$s</xliff:g> për të menaxhuar profilin tënd të <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
- <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</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="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 fdbbe8e668f5..37af18534712 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</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="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 bfd25162aec6..f78fadf8d864 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfigurera &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tack"</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="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 437ae7f332e8..495c44111290 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Weka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hapana"</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="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 9b4a720a4863..20845bd69562 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
<string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string>
- <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</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="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 6e785de583aa..c855cf2a8501 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
<string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$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="2009764182871566255">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"అవును"</string>
- <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</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="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 b727d42035dd..d78ada26cd8a 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
<string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ต้องใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g> <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string>
- <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</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="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 a93282a8fae0..03165b43ab0f 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Kailangan ang <xliff:g id="APP_NAME">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</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="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 3abe064d60bc..b2c1cf2fa832 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazınızı yönetecek şekilde ayarlayın"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Evet"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</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="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 161d95e127e8..61b78e9a0b18 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -20,8 +20,8 @@
<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="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
<string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, щоб керувати своїм пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</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="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 dce18152dff4..ee7992109a18 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو سیٹ کریں - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"آپ کے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
- <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</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="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 2ca27b530651..7221b6d24893 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
<string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmalarini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> zarur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
- <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</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="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 06a1ab6846ae..2819e1df75ba 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -20,8 +20,8 @@
<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>
<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="4751119145078041732">"Đặt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Cần có <xliff:g id="APP_NAME">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Có"</string>
- <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</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="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 12bfcf3629cd..1440c401673e 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"设为由&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"若要管理<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"好"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了"</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="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 0c583b211035..e3f1eb1249f1 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -16,12 +16,12 @@
<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="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</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>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"設定 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"必須使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</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="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 519f0e8a7082..9f4041dd0aff 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"如要管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</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="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 7721b54166f5..dc933ae21599 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -20,8 +20,8 @@
<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>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
<string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setha i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</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="consent_yes" msgid="8344487259618762872">"Vumela"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
</resources>
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
new file mode 100644
index 000000000000..eb1faa0aa25c
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * 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.net;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Type annotations for constants used in the connectivity API surface.
+ *
+ * The annotations are maintained in a separate class so that it can be built as
+ * a separate library that other modules can build against, as Typedef should not
+ * be exposed as SystemApi.
+ *
+ * @hide
+ */
+public final class ConnectivityAnnotations {
+ private ConnectivityAnnotations() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
+ ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
+ ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
+ })
+ public @interface MultipathPreference {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
+ })
+ public @interface RestrictBackgroundStatus {}
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index a65bf41210a1..2b8f049a36fc 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -11,33 +11,6 @@ android_library {
name: "SettingsLib",
- defaults: [
- "SettingsLibDependenciesWithoutWifiTracker",
- ],
-
- // TODO(b/149540986): revert this change.
- static_libs: [
- // All other dependent components should be put in
- // "SettingsLibDependenciesWithoutWifiTracker".
- "WifiTrackerLib",
- ],
-
- // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
- // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
-
- resource_dirs: ["res"],
-
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
-
- min_sdk_version: "29",
-
-}
-
-java_defaults {
- name: "SettingsLibDependenciesWithoutWifiTracker",
static_libs: [
"androidx.annotation_annotation",
"androidx.legacy_legacy-support-v4",
@@ -48,6 +21,7 @@ java_defaults {
"androidx.mediarouter_mediarouter-nodeps",
"iconloader",
+ "WifiTrackerLibRes",
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
"SettingsLibActionBarShadow",
@@ -74,6 +48,19 @@ java_defaults {
"SettingsLibTwoTargetPreference",
"SettingsLibSettingsTransition",
],
+
+ // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
+ // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
+
+ resource_dirs: ["res"],
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ min_sdk_version: "29",
+
}
// NOTE: Keep this module in sync with ./common.mk
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
new file mode 100644
index 000000000000..d7e778ff854b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Weier"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
new file mode 100644
index 000000000000..6701dea718ce
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"አሰናብት"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
new file mode 100644
index 000000000000..0f1b9acbd15b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"إغلاق"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
new file mode 100644
index 000000000000..21dd94c66e18
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"অগ্ৰাহ্য কৰক"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
new file mode 100644
index 000000000000..7f91eb49db71
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Qapadın"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..ca16c3de21db
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacite"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
new file mode 100644
index 000000000000..b0980ea12641
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Адхіліць"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
new file mode 100644
index 000000000000..cccbf964e53c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отхвърляне"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
new file mode 100644
index 000000000000..e0dfcf24700c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"বাতিল করুন"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
new file mode 100644
index 000000000000..5e46c6c28820
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacivanje"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
new file mode 100644
index 000000000000..81bb04803aea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
new file mode 100644
index 000000000000..ac7623ecbfa4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavřít"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
new file mode 100644
index 000000000000..8c185d97fede
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Luk"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
new file mode 100644
index 000000000000..006301b2e94e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Schließen"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
new file mode 100644
index 000000000000..65843b282770
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Παράβλεψη"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..e2dae5ec9bfb
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‎Dismiss‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..4816be6d6921
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Descartar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
new file mode 100644
index 000000000000..5e820238c259
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cerrar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
new file mode 100644
index 000000000000..a688723554bd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Loobu"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
new file mode 100644
index 000000000000..64dd1c5d52db
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Baztertu"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
new file mode 100644
index 000000000000..bd8985f81d35
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"رد شدن"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
new file mode 100644
index 000000000000..c3841576b140
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ohita"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..dd5889cbf983
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
new file mode 100644
index 000000000000..dd5889cbf983
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
new file mode 100644
index 000000000000..d7876261ebdd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
new file mode 100644
index 000000000000..1fe4c5c0c946
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"છોડી દો"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
new file mode 100644
index 000000000000..f66ee7fed130
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"खारिज करें"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
new file mode 100644
index 000000000000..f7e7cd0d930f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbaci"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
new file mode 100644
index 000000000000..1551c843bab4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Bezárás"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
new file mode 100644
index 000000000000..e014cce62eca
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Փակել"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
new file mode 100644
index 000000000000..607e8117515f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Tutup"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
new file mode 100644
index 000000000000..4afc614c5a11
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hunsa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
new file mode 100644
index 000000000000..81bb04803aea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
new file mode 100644
index 000000000000..aa4c669c8e2c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"סגירה"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
new file mode 100644
index 000000000000..b42f6e683dd9
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"閉じる"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
new file mode 100644
index 000000000000..7bde8b6dd0f8
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"უარყოფა"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
new file mode 100644
index 000000000000..01235e09182b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабу"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
new file mode 100644
index 000000000000..4e14820be7c7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ច្រានចោល"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
new file mode 100644
index 000000000000..b9a542045794
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ವಜಾಗೊಳಿಸಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
new file mode 100644
index 000000000000..9b5169976a64
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"닫기"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
new file mode 100644
index 000000000000..affb8ec93fdd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабуу"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
new file mode 100644
index 000000000000..7079f7c73ccb
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ປິດໄວ້"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
new file mode 100644
index 000000000000..4cee14a440ee
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Atsisakyti"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
new file mode 100644
index 000000000000..120a76286a90
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Nerādīt"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
new file mode 100644
index 000000000000..76a4390a6b72
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отфрли"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
new file mode 100644
index 000000000000..5a4e14c88c36
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
new file mode 100644
index 000000000000..3974470813e4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Үл хэрэгсэх"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
new file mode 100644
index 000000000000..4bd44859ec8f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"डिसमिस करा"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
new file mode 100644
index 000000000000..290323be8392
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ketepikan"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
new file mode 100644
index 000000000000..52ecc4940c7d
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ပယ်ရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
new file mode 100644
index 000000000000..c1e39a417a87
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Lukk"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
new file mode 100644
index 000000000000..15102541bb87
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"हटाउनुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
new file mode 100644
index 000000000000..920349ff6fd4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Sluiten"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
new file mode 100644
index 000000000000..36e7d3bdb216
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ଖାରଜ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
new file mode 100644
index 000000000000..250ef2e86aca
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ਖਾਰਜ ਕਰੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
new file mode 100644
index 000000000000..9ad630adc92d
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zamknij"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..80b70ae95655
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..d7876261ebdd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
new file mode 100644
index 000000000000..80b70ae95655
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
new file mode 100644
index 000000000000..18b6a0e4c830
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Respingeți"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
new file mode 100644
index 000000000000..b6946572c400
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрыть"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
new file mode 100644
index 000000000000..d818cf7db236
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ඉවත ලන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
new file mode 100644
index 000000000000..4f59f851e574
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavrieť"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
new file mode 100644
index 000000000000..1ca68bf0709c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Opusti"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
new file mode 100644
index 000000000000..dbe792759608
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hiq"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
new file mode 100644
index 000000000000..68a2d5bbf343
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Одбаците"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
new file mode 100644
index 000000000000..ef2df3ca45ea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorera"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
new file mode 100644
index 000000000000..ebb0c0228b3a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ondoa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
new file mode 100644
index 000000000000..9b175c7b92c4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"மூடும்"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
new file mode 100644
index 000000000000..22a6f59f091c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"విస్మరించు"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
new file mode 100644
index 000000000000..6546bfa57ee8
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ปิด"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
new file mode 100644
index 000000000000..9b944de688ef
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"I-dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
new file mode 100644
index 000000000000..96d49e941cde
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Kapat"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
new file mode 100644
index 000000000000..f51b0e75f9ea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрити"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
new file mode 100644
index 000000000000..ad3fafb14f3a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"برخاست کریں"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
new file mode 100644
index 000000000000..1e247457e554
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Yopish"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
new file mode 100644
index 000000000000..a30cdbf1829f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Đóng"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..a8f36e4db962
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"关闭"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..b9ee658dd4fa
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..b9ee658dd4fa
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
new file mode 100644
index 000000000000..80faa175920b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cashisa"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-af/strings.xml b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
new file mode 100644
index 000000000000..c17f3edc6744
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Kom meer te wete"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-am/strings.xml b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
new file mode 100644
index 000000000000..02e61312d77c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"የበለጠ ለመረዳት"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
new file mode 100644
index 000000000000..1f279a6a9f72
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزيد من المعلومات"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-as/strings.xml b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
new file mode 100644
index 000000000000..a34b474edac2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"অধিক জানক"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-az/strings.xml b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
new file mode 100644
index 000000000000..b49036ee0bbc
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ətraflı məlumat"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..993ec9a92182
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-be/strings.xml b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
new file mode 100644
index 000000000000..f9d6129ba1f6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Даведацца больш"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
new file mode 100644
index 000000000000..605663d33c0c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Научете повече"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
new file mode 100644
index 000000000000..c58142d72618
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"আরও জানুন"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
new file mode 100644
index 000000000000..993ec9a92182
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
new file mode 100644
index 000000000000..7abf10f24957
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Més informació"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
new file mode 100644
index 000000000000..decbb68efe5d
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Další informace"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-da/strings.xml b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
new file mode 100644
index 000000000000..81d1c7cf5d04
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Få flere oplysninger"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-de/strings.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
new file mode 100644
index 000000000000..fe885aa6f091
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Weitere Informationen"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-el/strings.xml b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
new file mode 100644
index 000000000000..5a30833222ad
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Μάθετε περισσότερα"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..bd12547ad6ff
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎Learn more‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..f31d9ea26005
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-es/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
new file mode 100644
index 000000000000..f31d9ea26005
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-et/strings.xml b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
new file mode 100644
index 000000000000..78b65edd9e57
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lisateave"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
new file mode 100644
index 000000000000..cf7fa003ca28
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lortu informazio gehiago"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
new file mode 100644
index 000000000000..464c58e02477
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"بیشتر بدانید"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
new file mode 100644
index 000000000000..856b96288cda
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lue lisää"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..6d856caab1ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
new file mode 100644
index 000000000000..6d856caab1ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
new file mode 100644
index 000000000000..cde57d868361
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Máis información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
new file mode 100644
index 000000000000..54249b88b47e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"વધુ જાણો"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
new file mode 100644
index 000000000000..95ae240d1ea0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ज़्यादा जानें"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
new file mode 100644
index 000000000000..993ec9a92182
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
new file mode 100644
index 000000000000..ae3c94809f7c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"További információ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
new file mode 100644
index 000000000000..de9137b0f241
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Իմանալ ավելին"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-in/strings.xml b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
new file mode 100644
index 000000000000..4b5cb16f95ca
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pelajari lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-is/strings.xml b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
new file mode 100644
index 000000000000..111094c201cd
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Nánar"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-it/strings.xml b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
new file mode 100644
index 000000000000..053c80c6c4ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Scopri di più"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
new file mode 100644
index 000000000000..55b01873c2fc
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"מידע נוסף"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
new file mode 100644
index 000000000000..3312cb430ed7
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"詳細"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
new file mode 100644
index 000000000000..67bb223f68ec
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"შეიტყვეთ მეტი"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
new file mode 100644
index 000000000000..db11a766e8a5
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Толығырақ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-km/strings.xml b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
new file mode 100644
index 000000000000..1977dd323b20
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ស្វែងយល់បន្ថែម"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
new file mode 100644
index 000000000000..47fa3d5482ba
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
new file mode 100644
index 000000000000..d8d220068c03
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"자세히 알아보기"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
new file mode 100644
index 000000000000..74c6a497e3d0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Кеңири маалымат"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
new file mode 100644
index 000000000000..2e4124b8eb42
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ສຶກສາເພີ່ມເຕີມ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
new file mode 100644
index 000000000000..2981c665abfe
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Sužinokite daugiau"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
new file mode 100644
index 000000000000..97663056591f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Uzzināt vairāk"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
new file mode 100644
index 000000000000..1f734c5a12a4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Дознајте повеќе"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
new file mode 100644
index 000000000000..1cd466bcce34
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"കൂടുതലറിയുക"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
new file mode 100644
index 000000000000..8bac1eb110a0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Нэмэлт мэдээлэл авах"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
new file mode 100644
index 000000000000..45387200bdc8
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"अधिक जाणून घ्या"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
new file mode 100644
index 000000000000..cd1b17a96ea4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ketahui lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-my/strings.xml b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
new file mode 100644
index 000000000000..751a87a33da2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုမိုလေ့လာရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
new file mode 100644
index 000000000000..08de00943699
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Finn ut mer"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
new file mode 100644
index 000000000000..ecfec36337ee
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"थप जान्नुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
new file mode 100644
index 000000000000..156408135ad1
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Meer informatie"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-or/strings.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
new file mode 100644
index 000000000000..e7924d646861
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
new file mode 100644
index 000000000000..1ce2ef246a36
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ਹੋਰ ਜਾਣੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
new file mode 100644
index 000000000000..5709f3e8cdee
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Więcej informacji"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..bc410ab8b62f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..bc410ab8b62f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
new file mode 100644
index 000000000000..bc410ab8b62f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
new file mode 100644
index 000000000000..2b5011764508
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Aflați mai multe"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
new file mode 100644
index 000000000000..bedde40f5ad7
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Подробнее…"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-si/strings.xml b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
new file mode 100644
index 000000000000..1a60601cf36c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"තව දැන ගන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
new file mode 100644
index 000000000000..c1008e5a433a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ďalšie informácie"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
new file mode 100644
index 000000000000..79e0a735b927
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Več o tem"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
new file mode 100644
index 000000000000..0fea476cfbb8
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Mëso më shumë"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
new file mode 100644
index 000000000000..9a73269b9d0e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Сазнајте више"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
new file mode 100644
index 000000000000..a78c3cbc6c0b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Läs mer"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
new file mode 100644
index 000000000000..52b1732043a3
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pata maelezo zaidi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
new file mode 100644
index 000000000000..75fc7c1adff2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"மேலும் அறிக"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-te/strings.xml b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
new file mode 100644
index 000000000000..6c8d6799754b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"మరింత తెలుసుకోండి"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-th/strings.xml b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
new file mode 100644
index 000000000000..025a2f0f3705
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ดูข้อมูลเพิ่มเติม"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
new file mode 100644
index 000000000000..4b6f830a4371
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Matuto pa"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
new file mode 100644
index 000000000000..77d151309c4c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Daha fazla bilgi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
new file mode 100644
index 000000000000..cec933d374a4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Докладніше"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
new file mode 100644
index 000000000000..1dceea7739eb
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزید جانیں"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
new file mode 100644
index 000000000000..58239498256a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Batafsil"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
new file mode 100644
index 000000000000..d6c46389fdfa
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Tìm hiểu thêm"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..446c8ce4bc4c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"了解详情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..8ab38c658c8f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..8ab38c658c8f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
new file mode 100644
index 000000000000..b53eb85428b4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Funda kabanzi"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
index 541a2468db45..281269ecd616 100644
--- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
@@ -223,7 +223,7 @@ public class HelpUtils {
*
* @return the uri with added query parameters
*/
- private static Uri uriWithAddedParameters(Context context, Uri baseUri) {
+ public static Uri uriWithAddedParameters(Context context, Uri baseUri) {
Uri.Builder builder = baseUri.buildUpon();
// Add in the preferred language
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
deleted file mode 100644
index 503d60c87bb9..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.wifi;
-
-import android.content.Context;
-
-import androidx.fragment.app.Fragment;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * WifiEntryPreference that can be long pressed.
- */
-public class LongPressWifiEntryPreference extends WifiEntryPreference {
-
- private final Fragment mFragment;
-
- public LongPressWifiEntryPreference(Context context, WifiEntry wifiEntry, Fragment fragment) {
- super(context, wifiEntry);
- mFragment = fragment;
- }
-
- @Override
- public void onBindViewHolder(final PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- if (mFragment != null) {
- view.itemView.setOnCreateContextMenuListener(mFragment);
- view.itemView.setTag(this);
- view.itemView.setLongClickable(true);
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
deleted file mode 100644
index 4dd3ff1f556c..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.wifi;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.R;
-import com.android.settingslib.Utils;
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * Preference to display a WifiEntry in a wifi picker.
- */
-public class WifiEntryPreference extends Preference implements WifiEntry.WifiEntryCallback,
- View.OnClickListener {
-
- private static final int[] STATE_SECURED = {
- R.attr.state_encrypted
- };
-
- private static final int[] FRICTION_ATTRS = {
- R.attr.wifi_friction
- };
-
- // These values must be kept within [WifiEntry.WIFI_LEVEL_MIN, WifiEntry.WIFI_LEVEL_MAX]
- private static final int[] WIFI_CONNECTION_STRENGTH = {
- R.string.accessibility_no_wifi,
- R.string.accessibility_wifi_one_bar,
- R.string.accessibility_wifi_two_bars,
- R.string.accessibility_wifi_three_bars,
- R.string.accessibility_wifi_signal_full
- };
-
- // StateListDrawable to display secured lock / metered "$" icon
- @Nullable private final StateListDrawable mFrictionSld;
- private final IconInjector mIconInjector;
- private WifiEntry mWifiEntry;
- private int mLevel = -1;
- private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true.
- private CharSequence mContentDescription;
- private OnButtonClickListener mOnButtonClickListener;
-
- public WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry) {
- this(context, wifiEntry, new IconInjector(context));
- }
-
- @VisibleForTesting
- WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry,
- @NonNull IconInjector iconInjector) {
- super(context);
-
- setLayoutResource(R.layout.preference_access_point);
- setWidgetLayoutResource(R.layout.access_point_friction_widget);
- mFrictionSld = getFrictionStateListDrawable();
- mWifiEntry = wifiEntry;
- mWifiEntry.setListener(this);
- mIconInjector = iconInjector;
- refresh();
- }
-
- public WifiEntry getWifiEntry() {
- return mWifiEntry;
- }
-
- @Override
- public void onBindViewHolder(final PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- final Drawable drawable = getIcon();
- if (drawable != null) {
- drawable.setLevel(mLevel);
- }
-
- view.itemView.setContentDescription(mContentDescription);
-
- // Turn off divider
- view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE);
-
- // Enable the icon button when the help string in this WifiEntry is not null.
- final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button);
- final ImageView frictionImageView = (ImageView) view.findViewById(
- R.id.friction_icon);
- if (mWifiEntry.getHelpUriString() != null
- && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
- final Drawable drawablehelp = getDrawable(R.drawable.ic_help);
- drawablehelp.setTintList(
- Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal));
- ((ImageView) imageButton).setImageDrawable(drawablehelp);
- imageButton.setVisibility(View.VISIBLE);
- imageButton.setOnClickListener(this);
- imageButton.setContentDescription(
- getContext().getText(R.string.help_label));
-
- if (frictionImageView != null) {
- frictionImageView.setVisibility(View.GONE);
- }
- } else {
- imageButton.setVisibility(View.GONE);
-
- if (frictionImageView != null) {
- frictionImageView.setVisibility(View.VISIBLE);
- bindFrictionImage(frictionImageView);
- }
- }
- }
-
- /**
- * Updates the title and summary; may indirectly call notifyChanged().
- */
- public void refresh() {
- setTitle(mWifiEntry.getTitle());
- final int level = mWifiEntry.getLevel();
- final boolean showX = mWifiEntry.shouldShowXLevelIcon();
- if (level != mLevel || showX != mShowX) {
- mLevel = level;
- mShowX = showX;
- updateIcon(mShowX, mLevel);
- notifyChanged();
- }
-
- setSummary(mWifiEntry.getSummary(false /* concise */));
- mContentDescription = buildContentDescription();
- }
-
- /**
- * Indicates the state of the WifiEntry has changed and clients may retrieve updates through
- * the WifiEntry getter methods.
- */
- public void onUpdated() {
- // TODO(b/70983952): Fill this method in
- refresh();
- }
-
- /**
- * Result of the connect request indicated by the WifiEntry.CONNECT_STATUS constants.
- */
- public void onConnectResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- /**
- * Result of the disconnect request indicated by the WifiEntry.DISCONNECT_STATUS constants.
- */
- public void onDisconnectResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- /**
- * Result of the forget request indicated by the WifiEntry.FORGET_STATUS constants.
- */
- public void onForgetResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- /**
- * Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants
- */
- public void onSignInResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- protected int getIconColorAttr() {
- final boolean accent = (mWifiEntry.hasInternetAccess()
- && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
- return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
- }
-
- private void updateIcon(boolean showX, int level) {
- if (level == -1) {
- setIcon(null);
- return;
- }
-
- final Drawable drawable = mIconInjector.getIcon(showX, level);
- if (drawable != null) {
- drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), getIconColorAttr()));
- setIcon(drawable);
- } else {
- setIcon(null);
- }
- }
-
- @Nullable
- private StateListDrawable getFrictionStateListDrawable() {
- TypedArray frictionSld;
- try {
- frictionSld = getContext().getTheme().obtainStyledAttributes(FRICTION_ATTRS);
- } catch (Resources.NotFoundException e) {
- // Fallback for platforms that do not need friction icon resources.
- frictionSld = null;
- }
- return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
- }
-
- /**
- * Binds the friction icon drawable using a StateListDrawable.
- *
- * <p>Friction icons will be rebound when notifyChange() is called, and therefore
- * do not need to be managed in refresh()</p>.
- */
- private void bindFrictionImage(ImageView frictionImageView) {
- if (frictionImageView == null || mFrictionSld == null) {
- return;
- }
- if ((mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE)
- && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)) {
- mFrictionSld.setState(STATE_SECURED);
- }
- frictionImageView.setImageDrawable(mFrictionSld.getCurrent());
- }
-
- /**
- * Helper method to generate content description string.
- */
- @VisibleForTesting
- CharSequence buildContentDescription() {
- final Context context = getContext();
-
- CharSequence contentDescription = getTitle();
- final CharSequence summary = getSummary();
- if (!TextUtils.isEmpty(summary)) {
- contentDescription = TextUtils.concat(contentDescription, ",", summary);
- }
- int level = mWifiEntry.getLevel();
- if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
- contentDescription = TextUtils.concat(contentDescription, ",",
- context.getString(WIFI_CONNECTION_STRENGTH[level]));
- }
- return TextUtils.concat(contentDescription, ",",
- mWifiEntry.getSecurity() == WifiEntry.SECURITY_NONE
- ? context.getString(R.string.accessibility_wifi_security_type_none)
- : context.getString(R.string.accessibility_wifi_security_type_secured));
- }
-
-
- static class IconInjector {
- private final Context mContext;
-
- IconInjector(Context context) {
- mContext = context;
- }
-
- public Drawable getIcon(boolean showX, int level) {
- return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
- }
- }
-
- /**
- * Set listeners, who want to listen the button client event.
- */
- public void setOnButtonClickListener(OnButtonClickListener listener) {
- mOnButtonClickListener = listener;
- notifyChanged();
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.icon_button) {
- if (mOnButtonClickListener != null) {
- mOnButtonClickListener.onButtonClick(this);
- }
- }
- }
-
- /**
- * Callback to inform the caller that the icon button is clicked.
- */
- public interface OnButtonClickListener {
-
- /**
- * Register to listen the button click event.
- */
- void onButtonClick(WifiEntryPreference preference);
- }
-
- private Drawable getDrawable(@DrawableRes int iconResId) {
- Drawable buttonIcon = null;
-
- try {
- buttonIcon = getContext().getDrawable(iconResId);
- } catch (Resources.NotFoundException exception) {
- // Do nothing
- }
- return buttonIcon;
- }
-
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
deleted file mode 100644
index c21830b28e3a..000000000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.wifi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.R;
-import com.android.wifitrackerlib.WifiEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class WifiEntryPreferenceTest {
-
- private Context mContext;
-
- @Mock
- private WifiEntry mMockWifiEntry;
- @Mock
- private WifiEntryPreference.IconInjector mMockIconInjector;
-
- @Mock
- private Drawable mMockDrawable0;
- @Mock
- private Drawable mMockDrawable1;
- @Mock
- private Drawable mMockDrawable2;
- @Mock
- private Drawable mMockDrawable3;
- @Mock
- private Drawable mMockDrawable4;
-
- @Mock
- private Drawable mMockShowXDrawable0;
- @Mock
- private Drawable mMockShowXDrawable1;
- @Mock
- private Drawable mMockShowXDrawable2;
- @Mock
- private Drawable mMockShowXDrawable3;
- @Mock
- private Drawable mMockShowXDrawable4;
-
- private static final String MOCK_TITLE = "title";
- private static final String MOCK_SUMMARY = "summary";
- private static final String FAKE_URI_STRING = "fakeuri";
-
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
-
- MockitoAnnotations.initMocks(this);
-
- when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
- when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
-
- when(mMockIconInjector.getIcon(false /* showX */, 0)).thenReturn(mMockDrawable0);
- when(mMockIconInjector.getIcon(false /* showX */, 1)).thenReturn(mMockDrawable1);
- when(mMockIconInjector.getIcon(false /* showX */, 2)).thenReturn(mMockDrawable2);
- when(mMockIconInjector.getIcon(false /* showX */, 3)).thenReturn(mMockDrawable3);
- when(mMockIconInjector.getIcon(false /* showX */, 4)).thenReturn(mMockDrawable4);
-
- when(mMockIconInjector.getIcon(true /* showX */, 0))
- .thenReturn(mMockShowXDrawable0);
- when(mMockIconInjector.getIcon(true /* showX */, 1))
- .thenReturn(mMockShowXDrawable1);
- when(mMockIconInjector.getIcon(true /* showX */, 2))
- .thenReturn(mMockShowXDrawable2);
- when(mMockIconInjector.getIcon(true /* showX */, 3))
- .thenReturn(mMockShowXDrawable3);
- when(mMockIconInjector.getIcon(true /* showX */, 4))
- .thenReturn(mMockShowXDrawable4);
- }
-
- @Test
- public void constructor_shouldSetWifiEntryTitleAndSummary() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- assertThat(pref.getTitle()).isEqualTo(MOCK_TITLE);
- assertThat(pref.getSummary()).isEqualTo(MOCK_SUMMARY);
- }
-
- @Test
- public void constructor_shouldSetIcon() {
- when(mMockWifiEntry.getLevel()).thenReturn(0);
-
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- assertThat(pref.getIcon()).isEqualTo(mMockDrawable0);
- }
-
- @Test
- public void titleChanged_refresh_shouldUpdateTitle() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final String updatedTitle = "updated title";
- when(mMockWifiEntry.getTitle()).thenReturn(updatedTitle);
-
- pref.refresh();
-
- assertThat(pref.getTitle()).isEqualTo(updatedTitle);
- }
-
- @Test
- public void summaryChanged_refresh_shouldUpdateSummary() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final String updatedSummary = "updated summary";
- when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary);
-
- pref.refresh();
-
- assertThat(pref.getSummary()).isEqualTo(updatedSummary);
- }
-
- @Test
- public void levelChanged_refresh_shouldUpdateLevelIcon() {
- final List<Drawable> iconList = new ArrayList<>();
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
-
- assertThat(iconList).containsExactly(mMockDrawable0, mMockDrawable1,
- mMockDrawable2, mMockDrawable3, mMockDrawable4, null);
- }
-
- @Test
- public void levelChanged_showXWifiRefresh_shouldUpdateLevelIcon() {
- final List<Drawable> iconList = new ArrayList<>();
- when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
-
- assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
- mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
- }
-
- @Test
- public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
- when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
- false);
- final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
- pref.onBindViewHolder(holder);
-
- assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() {
- when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
- false);
- final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
- pref.onBindViewHolder(holder);
-
- assertThat(view.findViewById(R.id.icon_button).getContentDescription()).isEqualTo(
- mContext.getString(R.string.help_label));
- }
-
- @Test
- public void subscriptionEntry_shouldSetImageButtonGone() {
- when(mMockWifiEntry.isSubscription()).thenReturn(true);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
- false);
- final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
- pref.onBindViewHolder(holder);
-
- assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.GONE);
- }
-}
diff --git a/packages/SettingsProvider/res/values-mcc466/defaults.xml b/packages/SettingsProvider/res/values-mcc466/defaults.xml
new file mode 100644
index 000000000000..fdeda8881dcc
--- /dev/null
+++ b/packages/SettingsProvider/res/values-mcc466/defaults.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Whether to enable mute when off body by default. -->
+ <bool name="def_wearable_muteWhenOffBodyEnabled">false</bool>
+</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 8e6e251ff3f2..a9bc3be44a29 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -214,6 +214,10 @@
<!-- Default for Settings.System.VIBRATE_WHEN_RINGING -->
<bool name="def_vibrate_when_ringing">false</bool>
+ <!-- Default for Settings.Global.CELL_ON; see PhoneConstants.CELL_ON_FLAG.
+ 0: cellular off; 1: cellular on. -->
+ <integer name="def_cell_on">1</integer>
+
<!-- Default for Settings.Global.APPLY_RAMPING_RINGER -->
<bool name="def_apply_ramping_ringer">false</bool>
@@ -259,4 +263,64 @@
<!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
<bool name="def_one_handed_mode_activated">false</bool>
+ <!-- ========================================== -->
+ <!-- Default values for wear specific settings. -->
+
+ <bool name="def_wearable_hotwordDetectionEnabled">false</bool>
+
+ <bool name="def_wearable_smartIlluminateEnabled">true</bool>
+
+ <integer name="def_wearable_offChargerWifiUsageLimitMinutes">120</integer>
+
+ <!-- Default enabled state of accelerometer-based up/down gestures. -->
+ <bool name="def_wearable_upDownGesturesEnabled">false</bool>
+
+ <!-- Whether to enable mute when off body by default. -->
+ <bool name="def_wearable_muteWhenOffBodyEnabled">true</bool>
+
+ <!-- Whether to use an alternate launcher if available. -->
+ <bool name="def_wearable_alternateLauncherEnabled">true</bool>
+
+ <!-- If a square screen, how rounded the corners are. Same as CSS border-radius property. -->
+ <integer name="def_wearable_squareScreenCornerRoundness">0</integer>
+
+ <!-- Side button present -->
+ <bool name="def_wearable_sideButtonPresent">true</bool>
+
+ <!-- Android wear version. This value is a string due to no long type in resources -->
+ <string name="def_wearable_androidWearVersion" translatable="false">2</string>
+
+ <!-- This value is the decimal representation of the capabilities bitmask as defined below:
+ 0000001 - WIFI
+ 0000010 - Accounts
+ 0000100 - Phone
+ 0001000 - Cell
+ 0010000 - Companion Legacy Calling
+ 0100000 - Speaker
+ 1000000 - Setup Protocomm Channel
+
+ Note: These must match the positions in
+ com.google.android.clockwork.common.system.WearSystemConstants -->
+ <string name="def_wearable_systemCapabilities" translatable="false">3</string>
+
+ <!-- This value is used for the default system capabilities used on LE device. -->
+ <string name="def_wearable_leSystemCapabilities" translatable="false">1</string>
+
+ <!-- Brightness levels, on a 0-255 scale -->
+ <string name="def_wearable_brightnessLevels" translatable="false">255,204,153,102,51</string>
+
+ <!-- Whether to allow mobile signal detector by default. -->
+ <bool name="def_wearable_mobileSignalDetectorAllowed">true</bool>
+
+ <!-- If ambient mode is enabled by default. -->
+ <bool name="def_wearable_ambientEnabled">true</bool>
+
+ <!-- Whether tilt to wake is enabled by default. -->
+ <bool name="def_wearable_tiltToWakeEnabled">true</bool>
+
+ <!-- Whether touch to wake is enabled by default. -->
+ <bool name="def_wearable_touchToWakeEnabled">true</bool>
+
+ <!-- Whether tilt to bright is enabled by default. -->
+ <bool name="def_wearable_tiltToBrightEnabled">false</bool>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index eb8196176034..b84a9cd5c5df 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -40,6 +40,9 @@ public class GlobalSettings {
public static final String[] SETTINGS_TO_BACKUP = {
Settings.Global.APPLY_RAMPING_RINGER,
Settings.Global.BUGREPORT_IN_POWER_MENU,
+ Settings.Global.CLOCKWORK_SYSUI_PACKAGE_NAME,
+ Settings.Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME,
+ Settings.Global.CLOCKWORK_HOME_READY,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
Settings.Global.AUTO_TIME,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 5220a04d73e6..b5a7c84a35d8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -113,6 +113,9 @@ public class GlobalSettingsValidators {
VALIDATORS.put(
Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_SYSUI_PACKAGE_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_SPECIFIER, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.SOFT_AP_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
@@ -140,6 +143,153 @@ public class GlobalSettingsValidators {
/* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
VALIDATORS.put(Global.DISABLE_WINDOW_BLURS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR);
+
+ VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.HOTWORD_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SMART_REPLIES_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.RETAIL_MODE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.RETAIL_MODE_CONSUMER),
+ String.valueOf(Global.Wearable.RETAIL_MODE_RETAIL)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.PLAY_STORE_AVAILABILITY,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.PLAY_STORE_AVAILABLE),
+ String.valueOf(Global.Wearable.PLAY_STORE_UNAVAILABLE),
+ String.valueOf(Global.Wearable.PLAY_STORE_AVAILABILITY_UNKNOWN)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.BUG_REPORT,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.BUG_REPORT_ENABLED),
+ String.valueOf(Global.Wearable.BUG_REPORT_DISABLED)
+ }));
+ VALIDATORS.put(Global.Wearable.SMART_ILLUMINATE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.CLOCKWORK_AUTO_TIME,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.SYNC_TIME_FROM_PHONE),
+ String.valueOf(Global.Wearable.SYNC_TIME_FROM_NETWORK),
+ String.valueOf(Global.Wearable.AUTO_TIME_OFF),
+ String.valueOf(Global.Wearable.INVALID_AUTO_TIME_STATE)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE),
+ String.valueOf(Global.Wearable.SYNC_TIME_ZONE_FROM_NETWORK),
+ String.valueOf(Global.Wearable.AUTO_TIME_ZONE_OFF),
+ String.valueOf(Global.Wearable.INVALID_AUTO_TIME_ZONE_STATE)
+ }));
+ VALIDATORS.put(Global.Wearable.CLOCKWORK_24HR_TIME, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AUTO_WIFI, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WIFI_POWER_SAVE, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS,
+ ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.UPDOWN_GESTURES_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.SETUP_SKIPPED,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.SETUP_SKIPPED_YES),
+ String.valueOf(Global.Wearable.SETUP_SKIPPED_NO),
+ String.valueOf(Global.Wearable.SETUP_SKIPPED_UNKNOWN)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.LAST_CALL_FORWARD_ACTION,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.CALL_FORWARD_ACTION_ON),
+ String.valueOf(Global.Wearable.CALL_FORWARD_ACTION_OFF),
+ String.valueOf(Global.Wearable.CALL_FORWARD_NO_LAST_ACTION)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.STEM_1_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+ String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.STEM_2_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+ String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.STEM_3_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+ String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+ }));
+ VALIDATORS.put(Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.ALTERNATE_LAUNCHER_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.CORNER_ROUNDNESS, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SIDE_BUTTON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.BUTTON_SET, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.ANDROID_WEAR_VERSION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SYSTEM_CAPABILITIES, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SYSTEM_EDITION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WEAR_PLATFORM_MR_NUMBER, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.BOTTOM_OFFSET, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.DISPLAY_SHAPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.DISPLAY_SHAPE_ROUND),
+ String.valueOf(Global.Wearable.DISPLAY_SHAPE_SQUARE)
+ }));
+ VALIDATORS.put(Global.Wearable.MOBILE_SIGNAL_DETECTOR, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_WAKE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_TOUCH_TO_WAKE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.DECOMPOSABLE_WATCHFACE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_GESTURE_SENSOR_ID, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_LOW_BIT_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_BRIGHT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_ANDROID),
+ String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.COMPANION_BLE_ROLE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.BLUETOOTH_ROLE_CENTRAL),
+ String.valueOf(Global.Wearable.BLUETOOTH_ROLE_PERIPHERAL)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.USER_HFP_CLIENT_SETTING,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.HFP_CLIENT_UNSET),
+ String.valueOf(Global.Wearable.HFP_CLIENT_ENABLED),
+ String.valueOf(Global.Wearable.HFP_CLIENT_DISABLED)
+ }));
+ VALIDATORS.put(Global.Wearable.HFP_CLIENT_PROFILE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.COMPANION_OS_VERSION, ANY_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 268603fa8b0d..cdf274f23dbb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2467,6 +2467,9 @@ class DatabaseHelper extends SQLiteOpenHelper {
loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
+ loadIntegerSetting(stmt, Settings.Global.CELL_ON,
+ R.integer.def_cell_on);
+
// Enable or disable Cell Broadcast SMS
loadSetting(stmt, Settings.Global.CDMA_CELL_BROADCAST_SMS,
RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 00fd19c2b984..f83ebaacb8e6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -33,6 +33,7 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.provider.Settings.Config.SyncDisabledMode;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -69,7 +70,7 @@ public final class DeviceConfigService extends Binder {
LIST,
RESET,
SET_SYNC_DISABLED_FOR_TESTS,
- IS_SYNC_DISABLED_FOR_TESTS,
+ GET_SYNC_DISABLED_FOR_TESTS,
}
MyShellCommand(SettingsProvider provider) {
@@ -103,8 +104,8 @@ public final class DeviceConfigService extends Binder {
verb = CommandVerb.RESET;
} else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
verb = CommandVerb.SET_SYNC_DISABLED_FOR_TESTS;
- } else if ("is_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
- verb = CommandVerb.IS_SYNC_DISABLED_FOR_TESTS;
+ } else if ("get_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.GET_SYNC_DISABLED_FOR_TESTS;
if (peekNextArg() != null) {
perr.println("Bad arguments");
return -1;
@@ -117,7 +118,7 @@ public final class DeviceConfigService extends Binder {
}
// Parse args for those commands that have them.
- int disableSyncMode = -1;
+ int syncDisabledModeArg = -1;
int resetMode = -1;
boolean makeDefault = false;
String namespace = null;
@@ -154,15 +155,10 @@ public final class DeviceConfigService extends Binder {
}
}
} else if (verb == CommandVerb.SET_SYNC_DISABLED_FOR_TESTS) {
- if (disableSyncMode == -1) {
- // DISABLE_SYNC_FOR_TESTS 1st arg (required)
- if ("none".equalsIgnoreCase(arg)) {
- disableSyncMode = SYNC_DISABLED_MODE_NONE;
- } else if ("persistent".equalsIgnoreCase(arg)) {
- disableSyncMode = SYNC_DISABLED_MODE_PERSISTENT;
- } else if ("until_reboot".equalsIgnoreCase(arg)) {
- disableSyncMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
- } else {
+ if (syncDisabledModeArg == -1) {
+ // SET_SYNC_DISABLED_FOR_TESTS 1st arg (required)
+ syncDisabledModeArg = parseSyncDisabledMode(arg);
+ if (syncDisabledModeArg == -1) {
// invalid
perr.println("Invalid sync disabled mode: " + arg);
return -1;
@@ -252,10 +248,16 @@ public final class DeviceConfigService extends Binder {
DeviceConfig.resetToDefaults(resetMode, namespace);
break;
case SET_SYNC_DISABLED_FOR_TESTS:
- DeviceConfig.setSyncDisabled(disableSyncMode);
+ DeviceConfig.setSyncDisabledMode(syncDisabledModeArg);
break;
- case IS_SYNC_DISABLED_FOR_TESTS:
- pout.println(DeviceConfig.isSyncDisabled());
+ case GET_SYNC_DISABLED_FOR_TESTS:
+ int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
+ String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
+ if (syncDisabledModeString == null) {
+ perr.println("Unknown mode: " + syncDisabledModeInt);
+ return -1;
+ }
+ pout.println(syncDisabledModeString);
break;
default:
perr.println("Unspecified command");
@@ -295,8 +297,9 @@ public final class DeviceConfigService extends Binder {
+ " syncing.");
pw.println(" persistent: Sync is disabled, this state will survive a reboot.");
pw.println(" until_reboot: Sync is disabled until the next reboot.");
- pw.println(" is_sync_disabled_for_tests");
- pw.println(" Prints 'true' if sync is disabled, 'false' otherwise.");
+ pw.println(" get_sync_disabled_for_tests");
+ pw.println(" Prints one of the SYNC_DISABLED_MODE values, see"
+ + " set_sync_disabled_for_tests");
}
private boolean delete(IContentProvider provider, String namespace, String key) {
@@ -358,4 +361,31 @@ public final class DeviceConfigService extends Binder {
}
}
}
+
+ private static @SyncDisabledMode int parseSyncDisabledMode(String arg) {
+ int syncDisabledMode;
+ if ("none".equalsIgnoreCase(arg)) {
+ syncDisabledMode = SYNC_DISABLED_MODE_NONE;
+ } else if ("persistent".equalsIgnoreCase(arg)) {
+ syncDisabledMode = SYNC_DISABLED_MODE_PERSISTENT;
+ } else if ("until_reboot".equalsIgnoreCase(arg)) {
+ syncDisabledMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
+ } else {
+ syncDisabledMode = -1;
+ }
+ return syncDisabledMode;
+ }
+
+ private static String formatSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
+ switch (syncDisabledMode) {
+ case SYNC_DISABLED_MODE_NONE:
+ return "none";
+ case SYNC_DISABLED_MODE_PERSISTENT:
+ return "persistent";
+ case SYNC_DISABLED_MODE_UNTIL_REBOOT:
+ return "until_reboot";
+ default:
+ return null;
+ }
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 9cd7083a2a11..8c669d21db81 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -848,9 +848,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
mRestoredFromSdkInt);
- if (DEBUG) {
- Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
- }
+ Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 073b4d00653d..579c3a59d3c5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -763,9 +763,6 @@ class SettingsProtoDumpUtil {
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
dumpSetting(s, p,
- Settings.Global.ANGLE_ALLOWLIST,
- GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST);
- dumpSetting(s, p,
Settings.Global.ANGLE_EGL_FEATURES,
GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1e3ee22fee2f..9e7daf49eaab 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -43,6 +43,7 @@ import android.app.backup.BackupManager;
import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.bluetooth.BluetoothProfile;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
@@ -85,6 +86,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -136,7 +138,6 @@ import java.util.regex.Pattern;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
-
/**
* <p>
* This class is a content provider that publishes the system settings.
@@ -471,16 +472,16 @@ public class SettingsProvider extends ContentProvider {
return result;
}
- case Settings.CALL_METHOD_SET_SYNC_DISABLED_CONFIG: {
+ case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG: {
final int mode = getSyncDisabledMode(args);
- setSyncDisabledConfig(mode);
+ setSyncDisabledModeConfig(mode);
break;
}
- case Settings.CALL_METHOD_IS_SYNC_DISABLED_CONFIG: {
+ case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG: {
Bundle result = new Bundle();
- result.putBoolean(Settings.KEY_CONFIG_IS_SYNC_DISABLED_RETURN,
- isSyncDisabledConfig());
+ result.putInt(Settings.KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN,
+ getSyncDisabledModeConfig());
return result;
}
@@ -1148,7 +1149,7 @@ public class SettingsProvider extends ContentProvider {
final String callingPackage = resolveCallingPackage();
synchronized (mLock) {
- if (isSyncDisabledConfigLocked()) {
+ if (getSyncDisabledModeConfigLocked() != SYNC_DISABLED_MODE_NONE) {
return SET_ALL_RESULT_DISABLED;
}
final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
@@ -1158,32 +1159,32 @@ public class SettingsProvider extends ContentProvider {
}
}
- private void setSyncDisabledConfig(@SyncDisabledMode int syncDisabledMode) {
+ private void setSyncDisabledModeConfig(@SyncDisabledMode int syncDisabledMode) {
if (DEBUG) {
- Slog.v(LOG_TAG, "setSyncDisabledConfig(" + syncDisabledMode + ")");
+ Slog.v(LOG_TAG, "setSyncDisabledModeConfig(" + syncDisabledMode + ")");
}
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
synchronized (mLock) {
- setSyncDisabledConfigLocked(syncDisabledMode);
+ setSyncDisabledModeConfigLocked(syncDisabledMode);
}
}
- private boolean isSyncDisabledConfig() {
+ private int getSyncDisabledModeConfig() {
if (DEBUG) {
- Slog.v(LOG_TAG, "isSyncDisabledConfig");
+ Slog.v(LOG_TAG, "getSyncDisabledModeConfig");
}
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
synchronized (mLock) {
- return isSyncDisabledConfigLocked();
+ return getSyncDisabledModeConfigLocked();
}
}
@GuardedBy("mLock")
- private void setSyncDisabledConfigLocked(@SyncDisabledMode int syncDisabledMode) {
+ private void setSyncDisabledModeConfigLocked(@SyncDisabledMode int syncDisabledMode) {
boolean persistentValue;
boolean inMemoryValue;
if (syncDisabledMode == SYNC_DISABLED_MODE_NONE) {
@@ -1215,13 +1216,13 @@ public class SettingsProvider extends ContentProvider {
}
@GuardedBy("mLock")
- private boolean isSyncDisabledConfigLocked() {
+ private int getSyncDisabledModeConfigLocked() {
// Check the values used for both SYNC_DISABLED_MODE_PERSISTENT and
// SYNC_DISABLED_MODE_UNTIL_REBOOT.
// The SYNC_DISABLED_MODE_UNTIL_REBOOT value is cheap to check first.
if (mSyncConfigDisabledUntilReboot) {
- return true;
+ return SYNC_DISABLED_MODE_UNTIL_REBOOT;
}
// Now check the global setting used to implement SYNC_DISABLED_MODE_PERSISTENT.
@@ -1231,10 +1232,12 @@ public class SettingsProvider extends ContentProvider {
SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
Global.DEVICE_CONFIG_SYNC_DISABLED);
if (settingLocked == null) {
- return false;
+ return SYNC_DISABLED_MODE_NONE;
}
String settingValue = settingLocked.getValue();
- return settingValue != null && !"0".equals(settingValue);
+ boolean isSyncDisabledPersistent = settingValue != null && !"0".equals(settingValue);
+ return isSyncDisabledPersistent
+ ? SYNC_DISABLED_MODE_PERSISTENT : SYNC_DISABLED_MODE_NONE;
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -3585,7 +3588,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 203;
+ private static final int SETTINGS_VERSION = 204;
private final int mUserId;
@@ -5189,6 +5192,199 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 203;
}
+ if (currentVersion == 203) {
+ // Version 203: initialize entries migrated from wear settings provide.
+ initGlobalSettingsDefaultValForWearLocked(Global.Wearable.HAS_PAY_TOKENS,
+ false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, 6);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.HOTWORD_DETECTION_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_hotwordDetectionEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SMART_REPLIES_ENABLED, false);
+ Setting locationMode = getSecureSettingsLocked(userId)
+ .getSettingLocked(Secure.LOCATION_MODE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+ !locationMode.isNull()
+ && !Integer.toString(Secure.LOCATION_MODE_OFF)
+ .equals(locationMode.getValue()));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.RETAIL_MODE, Global.Wearable.RETAIL_MODE_CONSUMER);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.PLAY_STORE_AVAILABILITY,
+ Global.Wearable.PLAY_STORE_AVAILABILITY_UNKNOWN);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.BUG_REPORT,
+ "user".equals(Build.TYPE) // is user build?
+ ? Global.Wearable.BUG_REPORT_DISABLED
+ : Global.Wearable.BUG_REPORT_ENABLED);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SMART_ILLUMINATE_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_smartIlluminateEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CLOCKWORK_AUTO_TIME,
+ Global.Wearable.SYNC_TIME_FROM_PHONE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+ Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CLOCKWORK_24HR_TIME, false);
+ initGlobalSettingsDefaultValForWearLocked(Global.Wearable.AUTO_WIFI, true);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.WIFI_POWER_SAVE,
+ getContext()
+ .getResources()
+ .getInteger(
+ R.integer
+ .def_wearable_offChargerWifiUsageLimitMinutes));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS, 0L);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.UPDOWN_GESTURES_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_upDownGesturesEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SETUP_SKIPPED,
+ Global.Wearable.SETUP_SKIPPED_UNKNOWN);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.LAST_CALL_FORWARD_ACTION,
+ Global.Wearable.CALL_FORWARD_NO_LAST_ACTION);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_muteWhenOffBodyEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.WEAR_OS_VERSION_STRING, "");
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.ALTERNATE_LAUNCHER_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_alternateLauncherEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CORNER_ROUNDNESS,
+ getContext()
+ .getResources()
+ .getInteger(
+ R.integer
+ .def_wearable_squareScreenCornerRoundness));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.BUTTON_SET, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SIDE_BUTTON,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_sideButtonPresent));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.ANDROID_WEAR_VERSION,
+ Long.parseLong(
+ getContext()
+ .getResources()
+ .getString(
+ R.string.def_wearable_androidWearVersion)));
+ final int editionGlobal = 1;
+ final int editionLocal = 2;
+ boolean isLe =
+ getContext().getPackageManager().hasSystemFeature("cn.google");
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SYSTEM_EDITION,
+ isLe ? editionLocal : editionGlobal);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SYSTEM_CAPABILITIES,
+ getWearSystemCapabilities(isLe));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
+ SystemProperties.getInt("ro.cw_build.platform_mr", 0));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.BOTTOM_OFFSET, 0);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.DISPLAY_SHAPE,
+ Settings.Global.Wearable.DISPLAY_SHAPE_SQUARE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
+ getContext()
+ .getResources()
+ .getString(R.string.def_wearable_brightnessLevels));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
+ getContext()
+ .getResources()
+ .getBoolean(
+ R.bool.def_wearable_mobileSignalDetectorAllowed));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_ambientEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_TILT_TO_WAKE,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_tiltToWakeEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_touchToWakeEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_tiltToBrightEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.DECOMPOSABLE_WATCHFACE, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
+ SystemProperties.getBoolean("ro.ambient.force_when_docked", false));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
+ SystemProperties.getInt("ro.ambient.gesture_sensor_id", 0));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
+ SystemProperties.getBoolean("ro.ambient.low_bit_enabled", false));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
+ SystemProperties.getInt("ro.ambient.plugged_timeout_min", -1));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.COMPANION_ADDRESS, "");
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+ Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+ Settings.Global.Wearable.HFP_CLIENT_UNSET);
+ Setting disabledProfileSetting =
+ getGlobalSettingsLocked()
+ .getSettingLocked(
+ Settings.Global.BLUETOOTH_DISABLED_PROFILES);
+ final long disabledProfileSettingValue =
+ disabledProfileSetting.isNull()
+ ? 0
+ : Long.parseLong(disabledProfileSetting.getValue());
+ final boolean isHfpClientProfileEnabled =
+ (disabledProfileSettingValue
+ & (1 << BluetoothProfile.HEADSET_CLIENT))
+ == 0;
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
+ isHfpClientProfileEnabled);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.COMPANION_OS_VERSION,
+ Settings.Global.Wearable.COMPANION_OS_VERSION_UNDEFINED);
+
+ // TODO(b/164398026): add necessary initialization logic for all entries.
+ currentVersion = 204;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
@@ -5205,6 +5401,58 @@ public class SettingsProvider extends ContentProvider {
// Return the current version.
return currentVersion;
}
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, boolean val) {
+ initGlobalSettingsDefaultValForWearLocked(key, val ? "1" : "0");
+ }
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, int val) {
+ initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+ }
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, long val) {
+ initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+ }
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, String val) {
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ Setting currentSetting = globalSettings.getSettingLocked(key);
+ if (currentSetting.isNull()) {
+ globalSettings.insertSettingOverrideableByRestoreLocked(
+ key,
+ val,
+ null /* tag */,
+ true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ private long getWearSystemCapabilities(boolean isLe) {
+ // Capability constants are imported from
+ // com.google.android.clockwork.common.system.WearableConstants.
+ final int capabilityCompanionLegacyCalling = 5;
+ final int capabilitySpeaker = 6;
+ final int capabilitySetupProtocommChannel = 7;
+ long capabilities =
+ Long.parseLong(
+ getContext().getResources()
+ .getString(
+ isLe ? R.string.def_wearable_leSystemCapabilities
+ : R.string.def_wearable_systemCapabilities));
+ PackageManager pm = getContext().getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ capabilities |= getBitMask(capabilityCompanionLegacyCalling);
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ capabilities |= getBitMask(capabilitySpeaker);
+ }
+ capabilities |= getBitMask(capabilitySetupProtocommChannel);
+ return capabilities;
+ }
+
+ private long getBitMask(int capability) {
+ return 1 << (capability - 1);
+ }
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index f04acd09ae14..4aee1641fc63 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -33,6 +33,7 @@ import android.os.Message;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.providers.settings.SettingsOperationProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -47,6 +48,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import libcore.io.IoUtils;
@@ -806,7 +808,14 @@ final class SettingsState {
final int settingCount = settings.size();
for (int i = 0; i < settingCount; i++) {
+
Setting setting = settings.valueAt(i);
+ if (setting.isTransient()) {
+ if (DEBUG_PERSISTENCE) {
+ Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
+ }
+ continue;
+ }
if (writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
@@ -843,16 +852,20 @@ final class SettingsState {
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
if (t instanceof IOException) {
- // we failed to create a directory, so log the permissions and existence
- // state for the settings file and directory
- logSettingsDirectoryInformation(destination.getBaseFile());
+ if (DEBUG) {
+ // we failed to create a directory, so log the permissions and existence
+ // state for the settings file and directory
+ logSettingsDirectoryInformation(destination.getBaseFile());
+ }
if (t.getMessage().contains("Couldn't create directory")) {
// attempt to create the directory with Files.createDirectories, which
// throws more informative errors than File.mkdirs.
Path parentPath = destination.getBaseFile().getParentFile().toPath();
try {
Files.createDirectories(parentPath);
- Slog.i(LOG_TAG, "Successfully created " + parentPath);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Successfully created " + parentPath);
+ }
} catch (Throwable t2) {
Slog.e(LOG_TAG, "Failed to write " + parentPath
+ " with Files.writeDirectories", t2);
@@ -1005,7 +1018,9 @@ final class SettingsState {
in = file.openRead();
} catch (FileNotFoundException fnfe) {
Slog.w(LOG_TAG, "No settings state " + mStatePersistFile);
- logSettingsDirectoryInformation(mStatePersistFile);
+ if (DEBUG) {
+ logSettingsDirectoryInformation(mStatePersistFile);
+ }
addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
return;
}
@@ -1016,7 +1031,7 @@ final class SettingsState {
// Settings file exists but is corrupted. Retry with the fallback file
final File statePersistFallbackFile = new File(
mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX);
- Slog.i(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+ Slog.w(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+ ", retrying with fallback file: " + statePersistFallbackFile);
try {
in = new AtomicFile(statePersistFallbackFile).openRead();
@@ -1302,6 +1317,14 @@ final class SettingsState {
/* resetToDefault */ true);
}
+ public boolean isTransient() {
+ switch (getTypeFromKey(getKey())) {
+ case SETTINGS_TYPE_GLOBAL:
+ return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
+ }
+ return false;
+ }
+
public boolean update(String value, boolean setDefault, String packageName, String tag,
boolean forceNonSystemPackage, boolean overrideableByRestore) {
return update(value, setDefault, packageName, tag, forceNonSystemPackage,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 3297937e3e75..a35ec99a1837 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -509,7 +509,6 @@ public class SettingsBackupTest {
Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
- Settings.Global.ANGLE_ALLOWLIST,
Settings.Global.ANGLE_EGL_FEATURES,
Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
@@ -593,7 +592,67 @@ public class SettingsBackupTest {
Settings.Global.CACHED_APPS_FREEZER_ENABLED,
Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
+ Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
+ Settings.Global.Wearable.HAS_PAY_TOKENS,
+ Settings.Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN,
+ Settings.Global.Wearable.HOTWORD_DETECTION_ENABLED,
+ Settings.Global.Wearable.SMART_REPLIES_ENABLED,
+ Settings.Global.Wearable.DEFAULT_VIBRATION,
+ Settings.Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+ Settings.Global.Wearable.RETAIL_MODE,
+ Settings.Global.Wearable.PLAY_STORE_AVAILABILITY,
+ Settings.Global.Wearable.BUG_REPORT,
+ Settings.Global.Wearable.SMART_ILLUMINATE_ENABLED,
+ Settings.Global.Wearable.CLOCKWORK_AUTO_TIME,
+ Settings.Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+ Settings.Global.Wearable.CLOCKWORK_24HR_TIME,
+ Settings.Global.Wearable.AUTO_WIFI,
+ Settings.Global.Wearable.WIFI_POWER_SAVE,
+ Settings.Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS,
+ Settings.Global.Wearable.UPDOWN_GESTURES_ENABLED,
+ Settings.Global.Wearable.SETUP_SKIPPED,
+ Settings.Global.Wearable.LAST_CALL_FORWARD_ACTION,
+ Settings.Global.Wearable.STEM_1_TYPE,
+ Settings.Global.Wearable.STEM_1_DATA,
+ Settings.Global.Wearable.STEM_1_DEFAULT_DATA,
+ Settings.Global.Wearable.STEM_2_TYPE,
+ Settings.Global.Wearable.STEM_2_DATA,
+ Settings.Global.Wearable.STEM_2_DEFAULT_DATA,
+ Settings.Global.Wearable.STEM_3_TYPE,
+ Settings.Global.Wearable.STEM_3_DATA,
+ Settings.Global.Wearable.STEM_3_DEFAULT_DATA,
+ Settings.Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
+ Settings.Global.Wearable.WEAR_OS_VERSION_STRING,
+ Settings.Global.Wearable.ALTERNATE_LAUNCHER_ENABLED,
+ Settings.Global.Wearable.CORNER_ROUNDNESS,
+ Settings.Global.Wearable.BUTTON_SET,
+ Settings.Global.Wearable.SIDE_BUTTON,
+ Settings.Global.Wearable.ANDROID_WEAR_VERSION,
+ Settings.Global.Wearable.SYSTEM_CAPABILITIES,
+ Settings.Global.Wearable.SYSTEM_EDITION,
+ Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
+ Settings.Global.Wearable.COMPANION_BT_ADDRESS_DUAL,
+ Settings.Global.Wearable.DISPLAY_SHAPE,
+ Settings.Global.Wearable.BOTTOM_OFFSET,
+ Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
+ Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
+ Settings.Global.Wearable.AMBIENT_ENABLED,
+ Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
+ Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV,
+ Settings.Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
+ Settings.Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+ Settings.Global.Wearable.DECOMPOSABLE_WATCHFACE,
+ Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
+ Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
+ Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
+ Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
+ Settings.Global.Wearable.COMPANION_ADDRESS,
+ Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+ Settings.Global.Wearable.COMPANION_BLE_ROLE,
+ Settings.Global.Wearable.COMPANION_NAME,
+ Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+ Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
+ Settings.Global.Wearable.COMPANION_OS_VERSION);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
@@ -763,8 +822,10 @@ public class SettingsBackupTest {
@Test
public void globalSettingsBackedUpOrDenied() {
+ Set<String> candidateSettings = getCandidateSettings(Settings.Global.class);
+ candidateSettings.addAll(getCandidateSettings(Settings.Global.Wearable.class));
checkSettingsBackedUpOrDenied(
- getCandidateSettings(Settings.Global.class),
+ candidateSettings,
newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
BACKUP_DENY_LIST_GLOBAL_SETTINGS);
}
@@ -792,8 +853,7 @@ public class SettingsBackupTest {
.that(intersect(settingsToBackup, denylist)).isEmpty();
}
- private static Set<String> getCandidateSettings(
- Class<? extends Settings.NameValueTable> clazz) {
+ private static Set<String> getCandidateSettings(Class<?> clazz) {
HashSet<String> result = new HashSet<String>();
for (Field field : clazz.getDeclaredFields()) {
if (looksLikeValidSetting(field)) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1581e240e1d7..54fb6475197a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -571,6 +571,10 @@
<!-- Permission required for GTS test - PendingSystemUpdateTest -->
<uses-permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" />
+ <!-- Permission required to run the `vm` tool which manages on-device virtual machines -->
+ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+ <uses-permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 504e18a1488e..56b940c534fb 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -256,6 +256,14 @@ public final class RingtonePickerActivity extends AlertActivity implements
}
setupAlert();
+
+ ListView listView = mAlert.getListView();
+ if (listView != null) {
+ // List view needs to gain focus in order for RSB to work.
+ if (!listView.requestFocus()) {
+ Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
+ }
+ }
}
@Override
public void onSaveInstanceState(Bundle outState) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d051290cb4b4..228ee4027107 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -63,6 +63,7 @@ android_library {
"res",
],
static_libs: [
+ "WifiTrackerLib",
"WindowManager-Shell",
"SystemUIAnimationLib",
"SystemUIPluginLib",
@@ -144,6 +145,7 @@ android_library {
"src/**/I*.aidl",
],
static_libs: [
+ "WifiTrackerLib",
"SystemUIAnimationLib",
"SystemUIPluginLib",
"SystemUISharedLib",
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 835471d5cb94..1cf14f2362de 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -1,5 +1,7 @@
set noparent
+# Bug component: 78010
+
dsandler@android.com
aaliomer@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0d18b8dea284..24bffa14de15 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -53,8 +53,9 @@
// Curious where your @Scenario tests will run?
//
- // @Ignore or @FlakyTest: nowhere
- // @Staging: in staged-postsubmit, but not postsubmit or presubmit
+ // @Ignore: nowhere
+ // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
+ // presubmit
// @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
// none of the above: in presubmit, postsubmit, and staged-postsubmit
//
@@ -98,9 +99,6 @@
},
{
"exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
index c34012dc85a8..d816b3a461cd 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
@@ -21,4 +21,8 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">88dp</dimen>
+
+ <dimen name="qs_header_system_icons_area_height">0dp</dimen>
+ <dimen name="qs_panel_padding_top">0dp</dimen>
+
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_arrow_forward.xml b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
new file mode 100644
index 000000000000..438e4c70dc71
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_forward.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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:autoMirrored="true"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M6.23,20.23l1.77,1.77l10,-10l-10,-10l-1.77,1.77l8.23,8.23z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
new file mode 100644
index 000000000000..2c34060ccd61
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2H9V6zM18,20H6V10h12V20zM12,17c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2C10,16.1 10.9,17 12,17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 000000000000..ac4c43bd35b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
new file mode 100644
index 000000000000..f38a36804bc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
+ android:strokeAlpha="0.3"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,10h2v8h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,20h2v2h-2z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_background.xml b/packages/SystemUI/res/drawable/internet_dialog_background.xml
new file mode 100644
index 000000000000..3ceb0f6ac06a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners android:radius="8dp" />
+ <solid android:color="?android:attr/colorBackground" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
new file mode 100644
index 000000000000..50267fda0b25
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
@@ -0,0 +1,30 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"/>
+ <corners android:radius="20dp"/>
+ <padding
+ android:left="8dp"
+ android:right="8dp"
+ android:top="4dp"
+ android:bottom="4dp" />
+ <solid android:color="@android:color/transparent" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
new file mode 100644
index 000000000000..14672ef3dcfe
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners
+ android:topLeftRadius="@dimen/internet_dialog_corner_radius"
+ android:topRightRadius="@dimen/internet_dialog_corner_radius"
+ android:bottomLeftRadius="@dimen/internet_dialog_corner_radius"
+ android:bottomRightRadius="@dimen/internet_dialog_corner_radius"/>
+ <solid android:color="?android:attr/colorBackground" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
new file mode 100644
index 000000000000..088e82bb4260
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
@@ -0,0 +1,26 @@
+<?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="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_state_off_color"/>
+ <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
new file mode 100644
index 000000000000..250188b892f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
@@ -0,0 +1,26 @@
+<?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="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_state_on_color"/>
+ <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
new file mode 100644
index 000000000000..b41762f7908e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_switch_thumb_margin"
+ android:left="@dimen/settingslib_switch_thumb_margin"
+ android:right="@dimen/settingslib_switch_thumb_margin"
+ android:bottom="@dimen/settingslib_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/settingslib_switch_thumb_size"
+ android:width="@dimen/settingslib_switch_thumb_size"/>
+ <solid
+ android:color="@color/settingslib_thumb_off_color"
+ android:alpha="?android:attr/disabledAlpha"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_off.xml b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
new file mode 100644
index 000000000000..8b69ad1b2493
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_switch_thumb_margin"
+ android:left="@dimen/settingslib_switch_thumb_margin"
+ android:right="@dimen/settingslib_switch_thumb_margin"
+ android:bottom="@dimen/settingslib_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/settingslib_switch_thumb_size"
+ android:width="@dimen/settingslib_switch_thumb_size"/>
+ <solid android:color="@color/settingslib_thumb_off_color"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
new file mode 100644
index 000000000000..0f27fc2f4ad8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_switch_thumb_margin"
+ android:left="@dimen/settingslib_switch_thumb_margin"
+ android:right="@dimen/settingslib_switch_thumb_margin"
+ android:bottom="@dimen/settingslib_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/settingslib_switch_thumb_size"
+ android:width="@dimen/settingslib_switch_thumb_size"/>
+ <solid android:color="@color/settingslib_state_on_color"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
new file mode 100644
index 000000000000..06bb779b91ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/settingslib_thumb_on" android:state_checked="true"/>
+ <item android:drawable="@drawable/settingslib_thumb_off" android:state_checked="false"/>
+ <item android:drawable="@drawable/settingslib_thumb_disabled" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
new file mode 100644
index 000000000000..15dfcb70e25e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
@@ -0,0 +1,26 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle"
+ android:width="@dimen/settingslib_switch_track_width"
+ android:height="@dimen/settingslib_switch_track_height">
+ <solid
+ android:color="@color/settingslib_track_off_color"
+ android:alpha="?android:attr/disabledAlpha"/>
+ <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_off_background.xml b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
new file mode 100644
index 000000000000..4d79a6e20776
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle"
+ android:width="@dimen/settingslib_switch_track_width"
+ android:height="@dimen/settingslib_switch_track_height">
+ <solid android:color="@color/settingslib_track_off_color"/>
+ <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
new file mode 100644
index 000000000000..c12d012a0508
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle"
+ android:width="@dimen/settingslib_switch_track_width"
+ android:height="@dimen/settingslib_switch_track_height">
+ <solid android:color="@color/settingslib_track_on_color"/>
+ <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_selector.xml b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
new file mode 100644
index 000000000000..a38c3b4241a3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/settingslib_track_on_background" android:state_checked="true"/>
+ <item android:drawable="@drawable/settingslib_track_off_background" android:state_checked="false"/>
+ <item android:drawable="@drawable/settingslib_track_disabled_background" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
index 5588fd391681..2430eec77678 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
@@ -13,12 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/global_actions_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:orientation="vertical"
android:gravity="center"
android:layout_gravity="center">
<com.android.systemui.globalactions.GlobalActionsLayoutLite
@@ -28,11 +29,8 @@
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- android:layout_weight="1">
+ android:background="@drawable/global_actions_lite_background"
+ android:padding="@dimen/global_actions_lite_padding">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -40,8 +38,6 @@
android:gravity="center"
android:translationZ="@dimen/global_actions_translate"
android:orientation="horizontal"
- android:background="@drawable/global_actions_lite_background"
- android:padding="@dimen/global_actions_lite_padding"
android:layoutDirection="ltr">
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/list_flow"
@@ -57,4 +53,4 @@
app:flow_horizontalStyle="packed"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.globalactions.GlobalActionsLayoutLite>
-</androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
new file mode 100644
index 000000000000..7f22b71832a3
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -0,0 +1,366 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/internet_connectivity_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/internet_dialog_rounded_top_corner_background"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ style="@style/Widget.SliceView.Panel"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_marginTop="24dp"
+ android:layout_marginBottom="24dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/internet_dialog_title"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="google-sans"
+ android:textSize="24sp"/>
+
+ <TextView
+ android:id="@+id/internet_dialog_subtitle"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="20dp"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:fontFamily="google-sans"
+ android:textSize="14sp"/>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"/>
+
+ <ProgressBar
+ android:id="@+id/wifi_searching_progress"
+ android:indeterminate="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="1dp"
+ android:maxHeight="1dp"
+ style="@*android:style/Widget.Material.ProgressBar.Horizontal"/>
+
+ <androidx.core.widget.NestedScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/scroll_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@+id/internet_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/mobile_network_layout"
+ android:layout_width="match_parent"
+ android:layout_height="88dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:layout_gravity="center_vertical|start"
+ android:orientation="horizontal"
+ android:layout_marginRight="@dimen/settingslib_switchbar_margin"
+ android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:paddingStart="19dp"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start">
+ <ImageView
+ android:id="@+id/signal_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="1"
+ android:id="@+id/mobile_network_list"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/mobile_title"
+ android:layout_marginLeft="17dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:fontFamily="google-sans"/>
+ <TextView
+ android:id="@+id/mobile_summary"
+ android:layout_marginLeft="17dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_gravity="end|center_vertical">
+ <Switch
+ android:id="@+id/mobile_toggle"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/turn_on_wifi_layout"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginRight="@dimen/settingslib_switchbar_margin"
+ android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <FrameLayout
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView
+ android:text="@string/turn_on_wifi"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:fontFamily="google-sans"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp">
+ <Switch
+ android:id="@+id/wifi_toggle"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/wifi_connected_layout"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:layout_gravity="center_vertical|start"
+ android:clickable="true"
+ android:focusable="true"
+ android:visibility="gone"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:layout_marginRight="@dimen/settingslib_switchbar_margin"
+ android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start">
+ <ImageView
+ android:id="@+id/wifi_connected_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="3"
+ android:id="@+id/wifi_connected_list"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="72dp"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/wifi_connected_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ <TextView
+ android:id="@+id/wifi_connected_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:layout_marginRight="5dp"
+ android:clickable="false"
+ android:gravity="center">
+ <ImageView
+ android:id="@+id/wifi_settings_icon"
+ android:src="@drawable/ic_settings_24dp"
+ android:layout_width="24dp"
+ android:layout_gravity="center"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/wifi_list_layout"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:overScrollMode="never"
+ android:nestedScrollingEnabled="false"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/see_all_layout"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical|center_horizontal"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="16dp">
+ <ImageView
+ android:id="@+id/arrow_forward"
+ android:src="@drawable/ic_arrow_forward"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="16dp">
+ <TextView
+ android:text="@string/see_all_networks"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <Space
+ android:id="@+id/space"
+ android:layout_width="match_parent"
+ android:layout_height="28dp"
+ android:visibility="gone"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginBottom="25dp"
+ android:gravity="end"
+ android:orientation="horizontal">
+ <Button
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:id="@+id/done"
+ android:layout_width="60dp"
+ android:layout_height="30dp"
+ android:layout_marginRight="24dp"
+ android:layout_gravity="end"
+ android:background="@drawable/internet_dialog_footer_background"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/inline_done_button"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
new file mode 100644
index 000000000000..cb51ab6a3361
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -0,0 +1,99 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/internet_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/wifi_list"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:layout_gravity="center_vertical|start"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="16dp">
+ <ImageView
+ android:id="@+id/wifi_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="3"
+ android:id="@+id/wifi_network_layout"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="72dp">
+ <TextView
+ android:id="@+id/wifi_title"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_gravity="center_vertical"
+ android:gravity="start|center_vertical"
+ android:layout_marginLeft="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ <TextView
+ android:id="@+id/wifi_summary"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_gravity="center_vertical"
+ android:gravity="start|center_vertical"
+ android:layout_marginLeft="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:layout_marginRight="@dimen/settingslib_switchbar_padding_right"
+ android:clickable="false"
+ android:gravity="center">
+ <ImageView
+ android:id="@+id/wifi_locked_icon"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 8ca1b8e85634..3be99939ba0f 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -19,7 +19,7 @@
<View
android:id="@+id/customizer_transparent_view"
android:layout_width="match_parent"
- android:layout_height="@*android:dimen/quick_qs_offset_height"
+ android:layout_height="@dimen/qs_header_system_icons_area_height"
android:background="@android:color/transparent" />
<com.android.keyguard.AlphaOptimizedLinearLayout
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 5b9ca1b26158..74c39a335f4d 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -21,7 +21,7 @@
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:minHeight="48dp"
+ android:minHeight="@dimen/qs_header_row_min_height"
android:clickable="false"
android:focusable="true"
android:theme="@style/Theme.SystemUI.QuickSettings.Header">
@@ -31,7 +31,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="48dp"
- android:minHeight="48dp"
+ android:minHeight="@dimen/qs_header_row_min_height"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
@@ -43,7 +43,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:minHeight="48dp"
+ android:minHeight="@dimen/qs_header_row_min_height"
android:minWidth="48dp"
android:layout_marginStart="8dp"
android:layout_gravity="end|center_vertical"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index f9dcd390cd51..1be20d823007 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -42,21 +42,21 @@
android:layout_gravity="top"
android:clipChildren="false"
android:clipToPadding="false">
- <!-- Time, icons and Carrier (only in QS) -->
- <include layout="@layout/quick_qs_status_icons"/>
+ <!-- Time, icons and Carrier (only in QS) -->
+ <include layout="@layout/quick_qs_status_icons"/>
- <com.android.systemui.qs.QuickQSPanel
- android:id="@+id/quick_qs_panel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/quick_qs_status_icons"
- android:layout_marginTop="@dimen/qqs_layout_margin_top"
- android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:focusable="true"
- android:paddingBottom="10dp"
- android:importantForAccessibility="yes" />
+ <com.android.systemui.qs.QuickQSPanel
+ android:id="@+id/quick_qs_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/quick_qs_status_icons"
+ android:layout_marginTop="@dimen/qqs_layout_margin_top"
+ android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:paddingBottom="10dp"
+ android:importantForAccessibility="yes" />
</RelativeLayout>
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
index 194d2e063e97..1c3eedba4f6f 100644
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -14,16 +14,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
-<com.android.systemui.navigationbar.buttons.KeyButtonView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/rotate_suggestion"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="0"
- android:scaleType="center"
- android:visibility="invisible"
- android:contentDescription="@string/accessibility_rotate_button"
- android:paddingStart="@dimen/navigation_key_padding"
- android:paddingEnd="@dimen/navigation_key_padding"
-/> \ No newline at end of file
+ >
+
+ <com.android.systemui.navigationbar.buttons.KeyButtonView
+ android:id="@+id/rotate_suggestion"
+ android:layout_width="@dimen/floating_rotation_button_diameter"
+ android:layout_height="@dimen/floating_rotation_button_diameter"
+ android:contentDescription="@string/accessibility_rotate_button"
+ android:paddingStart="@dimen/navigation_key_padding"
+ android:paddingEnd="@dimen/navigation_key_padding"
+ android:layout_gravity="bottom|left"
+ android:scaleType="center"
+ android:visibility="invisible" />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/split_shade_header.xml
new file mode 100644
index 000000000000..401dc1955dbd
--- /dev/null
+++ b/packages/SystemUI/res/layout/split_shade_header.xml
@@ -0,0 +1,89 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/split_shade_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/split_shade_header_height"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:clickable="false"
+ android:focusable="true"
+ android:paddingLeft="@dimen/qs_panel_padding"
+ android:paddingRight="@dimen/qs_panel_padding"
+ android:visibility="gone"
+ android:theme="@style/Theme.SystemUI.QuickSettings.Header">
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="48dp"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:gravity="start|center_vertical"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status" />
+
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+ <FrameLayout
+ android:id="@+id/rightLayout"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="end">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|center_vertical">
+
+ <include
+ android:id="@+id/carrier_group"
+ layout="@layout/qs_carrier_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginStart="8dp"
+ android:focusable="false"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:minWidth="48dp" />
+
+ <com.android.systemui.statusbar.phone.StatusIconContainer
+ android:id="@+id/statusIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+
+ <com.android.systemui.BatteryMeterView
+ android:id="@+id/batteryRemainingIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ systemui:textAppearance="@style/TextAppearance.QS.Status" />
+ </LinearLayout>
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 1a912023e33c..cf91a2b3e588 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -67,6 +67,8 @@
android:clipToPadding="false"
android:clipChildren="false">
+ <include layout="@layout/split_shade_header"/>
+
<include
layout="@layout/keyguard_status_view"
android:visibility="gone"/>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 9598e87a2f40..c5578f23b5fb 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om oop te maak"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Stawing word vereis. Raak die vingerafdruksensor om te staaf."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Oproep aan die gang"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Vliegtuigmodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Gekoppel"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet sal nie outomaties koppel nie"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding nie"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen ander netwerke beskikbaar nie"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Geen netwerke beskikbaar nie"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netwerkbesonderhede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tik op \'n netwerk om te koppel"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Soek tans na netwerke …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kon nie aan netwerk koppel nie"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d9a3da392fc5..e64c7b41a553 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ለመክፈት የጣት አሻራ ይጠቀሙ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ማረጋገጥ ያስፈልጋል። ለማረጋገጥ የጣት አሻራ ዳሳሹን ይንኩ።"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"በመካሄድ ላይ የስልክ ጥሪ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"የአውሮፕላን ሁነታ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ተገናኝቷል"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"በይነመረብ በራስ-ሰር አይገናኝም"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ግንኙነት የለም"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ሌላ አውታረ መረብ የሉም"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ምንም አውታረ መረቦች የሉም"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"የአውታረ መረብ ዝርዝሮች"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ለመገናኘት አንድ አውታረ መረብ መታ ያድርጉ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index bfcfd9bd41d5..5e1468c1305f 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1185,4 +1185,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"يمكنك استخدام بصمة الإصبع للفتح"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"المصادقة مطلوبة. المس مستشعر بصمات الإصبع للمصادقة."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"مكالمة هاتفية جارية"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"وضع الطيران"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"بيانات الجوّال"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"متصلة بالإنترنت"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"لن يتم الاتصال بالإنترنت تلقائيًا."</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"لا يتوفّر اتصال بالإنترنت"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"لا تتوفّر شبكات أخرى."</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"لا تتوفّر أي شبكات."</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"تفاصيل الشبكة"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"انقر على إحدى الشبكات للاتصال بالإنترنت"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 316c87b18df0..f870bae050fe 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলিবলৈ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ আৱশ্যক। বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰিবলৈ ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"চলি থকা ফ’ন কল"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"এয়াৰপ্লেন ম\'ড"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"সংযোজিত হৈ আছে"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ইণ্টাৰনেট স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"সংযোগ নাই"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ৱাই-ফাই"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"নেটৱৰ্কৰ সবিশেষ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"সংযোগ কৰিবলৈ এটা নেটৱৰ্কত টিপক"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index ede3b1697fd7..1fdc227449c0 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmaq üçün barmaq izindən istifadə edin"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Doğrulanma tələb olunur. Doğrulamaq üçün barmaq izi sensoruna toxunun."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Davam edən zəng"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Uçuş rejimi"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Qoşulub"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"İnternet avtomatik qoşulmayacaq"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yoxdur"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Heç bir başqa şəbəkə əlçatan deyil"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Əlçatan şəbəkə yoxdur"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Şəbəkə məlumatları"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Qoşulmaq üçün şəbəkəyə toxunun"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index d48eebcd1cde..3c29f854b225 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1166,4 +1166,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je potvrda identiteta. Dodirnite senzor za otisak prsta da biste potvrdili identitet."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktuelni telefonski poziv"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Režim rada u avionu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Automatsko povezivanje na internet nije moguće"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Veza nije uspostavljena"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalji o mreži"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da biste se povezali"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 8cbc8bd31e41..75c52bf7a7bf 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1173,4 +1173,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Каб адкрыць, скарыстайце адбітак пальца"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Патрабуецца аўтэнтыфікацыя. Дакраніцеся да сканера адбіткаў пальцаў."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Бягучы тэлефонны выклік"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Рэжым палёту"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мабільная перадача даных"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Падключана"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Аўтаматычнае падключэнне да інтэрнэту адсутнічае"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма падключэння"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Больш няма даступных сетак"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Няма даступных сетак"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Інфармацыя пра сетку"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Каб падключыцца, націсніце на сетку"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index da457436df64..015504d48f29 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Използвайте отпечатък за отваряне"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Изисква се удостоверяване на самоличността. За целта докоснете сензора за отпечатъци."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущо телефонно обаждане"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Самолетен режим"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Свързано"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Няма автоматично да се установи връзка с интернет"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма връзка"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Няма други налични мрежи"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Няма налични мрежи"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Подробности за мрежата"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Докоснете мрежа, за да се свържете"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 2fe583a6abc7..69f42e774706 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"যাচাইকরণ করতে হবে। যাচাইকরণ করতে আঙুলের ছাপের সেন্সরে টাচ করুন।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ব্যবহারকারী এখন ফোনে কথা বলছেন"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"বিমান মোড"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"কানেক্ট করা আছে"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ইন্টারনেট অটোমেটিক কানেক্ট হবে না"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"কানেকশন নেই"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ওয়াই-ফাই"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"নেটওয়ার্কের বিবরণ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"কানেক্ট করতে একটি নেটওয়ার্কে ট্যাপ করুন"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 2dc7301d5e45..20fc2c67a910 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1166,4 +1166,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor za otisak prsta da autentificirate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u toku"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Način rada u avionu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nije se moguće automatski povezati s internetom"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani s mrežom"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Druge mreže nisu dostupne"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalji o mreži"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da se povežete"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index abd4fddaee25..2c95cd466982 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilitza l\'empremta digital per obrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticació necessària. Toca el sensor d\'empremtes digitals per autenticar-te."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Trucada en curs"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode d\'avió"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connectat"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet no es connectarà automàticament"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sense connexió"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hi ha cap altra xarxa disponible"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No hi ha cap xarxa disponible"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalls de la xarxa"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca una xarxa per connectar-te"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 25f796c0324f..b4937c6b2c2a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1172,4 +1172,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"K otevření použijte otisk prstu"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Je vyžadováno ověření. Dotkněte se snímače otisků prstů."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Probíhající hovor"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Režim Letadlo"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Připojeno"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet se nebude automaticky připojovat"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Žádné připojení"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Žádné další sítě nejsou k dispozici"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nejsou k dispozici žádné sítě"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Podrobnosti sítě"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Klepněte na síť, ke které se chcete připojit"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 06f6a35199e4..28912a7bfe6f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykslæseren for at godkende."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Igangværende telefonopkald"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flytilstand"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Forbundet"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Der oprettes ikke automatisk internetforbindelse"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Der er ingen forbindelse"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Der er ingen andre tilgængelige netværk"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Der er ingen tilgængelige netværk"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netværksoplysninger"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tryk på et netværk for at oprette forbindelse"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a92dd6fd9648..5b312752f699 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Mit Fingerabdruck öffnen"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentifizierung erforderlich. Tippe dazu einfach auf den Fingerabdrucksensor."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktiver Anruf"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flugmodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbunden"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Keine automatische Verbindung mit dem Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Keine Verbindung"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Keine anderen Netzwerke verfügbar"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Keine Netzwerke verfügbar"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WLAN"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netzwerkdetails"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tippe auf ein Netzwerk, um eine Verbindung herzustellen"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0e602645c1f7..de23a882255e 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Χρήση δακτυλικού αποτυπώματος για άνοιγμα"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Απαιτείται έλεγχος ταυτότητας. Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων για έλεγχο ταυτότητας."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Τηλεφωνική κλήση σε εξέλιξη"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Λειτουργία πτήσης"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Συνδέθηκε"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Χωρίς αυτόματη σύνδεση στο διαδίκτυο"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Χωρίς σύνδεση"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Δεν υπάρχουν άλλα διαθέσιμα δίκτυα"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Δεν υπάρχουν διαθέσιμα δίκτυα"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Λεπτομέρειες δικτύου"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Πατήστε ένα δίκτυο για να συνδεθείτε"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fd540633bb0b..38630e1a137c 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index dff3c3035a43..0669461a30dc 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fd540633bb0b..38630e1a137c 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fd540633bb0b..38630e1a137c 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 97976dbdc1c0..566831702c0c 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎Use fingerprint to open‎‏‎‎‏‎"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎Authentication required. Touch the fingerprint sensor to authenticate.‎‏‎‎‏‎"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎Ongoing phone call‎‏‎‎‏‎"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎Airplane mode‎‏‎‎‏‎"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎Mobile data‎‏‎‎‏‎"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="STATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ / ‎‏‎‎‏‏‎<xliff:g id="NETWORKMODE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎Connected‎‏‎‎‏‎"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎Internet won\'t auto‑connect‎‏‎‎‏‎"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎No connection‎‏‎‎‏‎"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎No other networks available‎‏‎‎‏‎"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎No networks available‎‏‎‎‏‎"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎Wi‑Fi‎‏‎‎‏‎"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎Network details‎‏‎‎‏‎"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎Tap a network to connect‎‏‎‎‏‎"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎Searching for networks…‎‏‎‎‏‎"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎Failed to connect to network‎‏‎‎‏‎"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎See all‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index b5b82c8ed2b4..fd66af0b9453 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella dactilar para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Se requiere de una autenticación. Toca el sensor de huellas dactilares para autenticarte."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo de avión"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conexión establecida"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"No se conectará automáticamente a Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No hay redes disponibles"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles de la red"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Presiona una red para conectarte a ella"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Se produjo un error al establecer conexión con la red"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 9cdfe0a2cfdb..c02f80f8a27f 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticación obligatoria. Toca el sensor de huellas digitales para autenticarte."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avión"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectada"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet no se conecta automáticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No hay redes disponibles"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles de la red"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca una red para conectarte"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 5e52394b02d8..b23b28346cb6 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Kasutage avamiseks sõrmejälge"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vajalik on autentimine. Puudutage autentimiseks sõrmejäljeandurit."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Käimasolev telefonikõne"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lennukirežiim"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilne andmeside"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ühendatud"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetiühendust ei looda automaatselt"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ühendus puudub"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ühtegi muud võrku pole saadaval"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ühtegi võrku pole saadaval"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Võrgu üksikasjad"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Puudutage ühendamiseks võrku"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 3e687d71b764..6344f6291a4f 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Erabili hatz-marka irekitzeko"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentifikazioa behar da. Autentifikatzeko, ukitu hatz-marken sentsorea."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefono-dei bat abian da"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Hegaldi modua"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> (<xliff:g id="NETWORKMODE">%2$s</xliff:g>)"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Konektatuta"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Ez da automatikoki konektatuko Internetera"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Konexiorik gabe"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ez dago beste sare erabilgarririk"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ez dago sare erabilgarririk"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifia"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Sarearen xehetasunak"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Sakatu sare bat hartara konektatzeko"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Sareak bilatzen…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ezin izan da konektatu sarera"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index b37a7be17247..6988df2300b6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"از اثر انگشت برای باز کردن قفل استفاده کنید"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالت‌سنجی لازم است. برای اصالت‌سنجی، حسگر اثر انگشت را لمس کنید."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"تماس تلفنی درحال انجام"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"حالت هواپیما"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"متصل است"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"اینترنت به‌طور خودکار متصل نخواهد شد"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"اتصال برقرار نیست"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"شبکه دیگری وجود ندارد"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"شبکه‌ای در دسترس نیست"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"جزئیات شبکه"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"برای اتصال به شبکه روی آن ضربه بزنید"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 60bb0a1bd9e8..0e7894a5e4a6 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Avaa sormenjäljellä"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Todennus vaaditaan. Todenna koskettamalla sormenjälkitunnistinta."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Puhelu käynnissä"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lentokonetila"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Yhdistetty"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetyhteyttä ei muodosteta automaattisesti"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ei yhteyttä"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ei muita verkkoja käytettävissä"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ei verkkoja käytettävissä"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Verkon tiedot"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Muodosta yhteys napauttamalla verkkoa"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 74ff58e0649f..decba941954a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Servez-vous de votre empreinte digitale pour ouvrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Touchez le capteur d\'empreintes digitales pour vous authentifier."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours…"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode Avion"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connexion active"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Connexion automatique à Internet impossible"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau n\'est accessible"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau n\'est accessible"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Détails du réseau"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Touchez un réseau pour vous y connecter"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3ef870471407..267f88e10022 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilisez votre empreinte pour ouvrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Appuyez sur le lecteur d\'empreintes digitales pour vous authentifier."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode Avion"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Pas de connexion automatique à Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau disponible"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau disponible"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Détails du réseau"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Appuyez sur un réseau pour vous connecter"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index ee23e949ba5a..703f102972c1 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa a impresión dixital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Requírese autenticación. Para autenticarte, toca o sensor de impresión dixital."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica en curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avión"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectada"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet non se conectará automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sen conexión"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Non hai outras redes dispoñibles"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Non hai redes dispoñibles"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca unha rede para conectarte a ela"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 59eceb7e0338..fa078b64fd65 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ખોલવા માટે ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"પ્રમાણીકરણ આવશ્યક છે. પ્રમાણિત કરવા માટે ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ફોન કૉલ ચાલુ છે"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"એરપ્લેન મોડ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"કનેક્ટ કરેલું"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ઇન્ટરનેટ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"કોઈ કનેક્શન નથી"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"બીજાં કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"વાઇ-ફાઇ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"નેટવર્કની વિગતો"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"કનેક્ટ કરવા માટે નેટવર્ક પર ટૅપ કરો"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 9ae4721d8aa0..761ad0513cc2 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"खोलने के लिए, फ़िंगरप्रिंट का इस्तेमाल करें"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि करना ज़रूरी है. पुष्टि करने के लिए, फ़िंगरप्रिंट सेंसर को छुएं."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फ़ोन कॉल चल रहा है"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"हवाई जहाज़ मोड"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट हो गया"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"इंटरनेट अपने-आप कनेक्ट नहीं होगा"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"इंटरनेट कनेक्शन नहीं है"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"कोई दूसरा नेटवर्क उपलब्ध नहीं है"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"कोई नेटवर्क उपलब्ध नहीं है"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"वाई-फ़ाई"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्क की जानकारी"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"इंटरनेट से कनेक्ट करने के लिए, किसी नेटवर्क पर टैप करें"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क खोजे जा रहे हैं…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्क से कनेक्ट नहीं किया जा सका"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 689a435fce62..bbba1a8fad32 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1166,4 +1166,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor otiska prsta da biste se autentificirali."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u tijeku"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Način rada u zrakoplovu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Neće biti automatskog povezivanja s internetom"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Pojedinosti o mreži"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da biste se povezali"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index d3c9f6fa2f26..78a8aee790c3 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ujjlenyomat használata a megnyitáshoz"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Hitelesítés szükséges. Érintse meg az ujjlenyomat-érzékelőt a hitelesítéshez."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Folyamatban lévő telefonhívás"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Repülős üzemmód"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiladat"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g>/<xliff:g id="STATE">%1$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Csatlakozva"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Az internetre történő csatlakozás nem automatikus"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nincs kapcsolat"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nincs több rendelkezésre álló hálózat"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nincs rendelkezésre álló hálózat"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Hálózati információk"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"A kapcsolódáshoz koppintson a kívánt hálózatra"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 4605a98dc5f5..95b2d6a48427 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Բացելու համար օգտագործեք մատնահետքը"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Պահանջվում է նույնականացում։ Դրա համար մատը հպեք մատնահետքի սկաներին։"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ընթացիկ հեռախոսազանգ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Ավիառեժիմ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Բջջային ինտերնետ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Միացած է"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Չհաջողվեց ավտոմատ միանալ համացանցին"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Կապ չկա"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Այլ հասանելի ցանցեր չկան"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Հասանելի ցանցեր չկան"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Ցանցի տվյալներ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Հպեք ցանցին՝ միանալու համար"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 227bf4d22d3e..2af2f246642c 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan sidik jari untuk membuka"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Perlu autentikasi. Sentuh sensor sidik jari untuk melakukan autentikasi."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telepon sedang berlangsung"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode pesawat"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data seluler"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Terhubung"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet tidak akan terhubung otomatis"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tidak ada koneksi"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Jaringan lain tidak tersedia"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Jaringan tidak tersedia"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detail jaringan"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ketuk jaringan untuk menghubungkan"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index e65097ef288c..dff08d16dab4 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Opna með fingrafari"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Auðkenningar krafist. Auðkenndu með því að snerta fingrafaralesarann."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Símtal í gangi"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flugstilling"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Tengt"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetið tengist ekki sjálfkrafa"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Engin tenging"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Engin önnur net í boði"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ekkert net í boði"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Upplýsingar um net"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ýttu á net til að tengjast"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 42e5deaa663a..6f160b926a52 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa l\'impronta per aprire"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticazione obbligatoria. Eseguila toccando il sensore di impronte digitali."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonata in corso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modalità aereo"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dati mobili"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connessione attiva"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet non si connetterà automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nessuna connessione"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nessun\'altra rete disponibile"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nessuna rete disponibile"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Dettagli rete"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tocca una rete per connetterti"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 65c23d47e043..73e1a74b1cdb 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1173,4 +1173,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"שימוש בטביעת אצבע כדי לפתוח"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"מתקיימת שיחה"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"מצב טיסה"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"מחובר"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"לא ניתן להתחבר לאינטרנט באופן אוטומטי"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"אין חיבור"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"אין רשתות זמינות אחרות"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"אין רשתות זמינות"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"פרטי הרשת"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"צריך להקיש על רשת כדי להתחבר"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d638ace6b459..1e8020fcc059 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"指紋を使って開いてください"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"認証が必要です。指紋認証センサーをタッチして認証してください。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"機内モード"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"モバイルデータ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"接続済み"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"インターネットに自動的に接続されません"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"接続なし"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"利用できるネットワークはありません"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ネットワークを利用できません"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ネットワークの詳細"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ネットワークをタップして接続"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 5ce3fe571bdd..b35decc0a043 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"გასახსნელად გამოიყენეთ თითის ანაბეჭდი"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"საჭიროა ავტორიზაცია. ავტორიზაციისთვის შეეხეთ თითის ანაბეჭდის სენსორს."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"მიმდინარე სატელეფონო ზარი"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"თვითმფრინავის რეჟიმი"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"მობილური ინტერნეტი"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"დაკავშირებული"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ინტერნეტს ავტომატურად არ დაუკავშირდება"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"კავშირი არ არის"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"სხვა ქსელები მიუწვდომელია"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ქსელები მიუწვდომელია"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ქსელის დეტალები"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"დასაკავშირებლად შეეხეთ ქსელს"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index cba176d5953b..e192c989e759 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ашу үшін саусақ ізін пайдаланыңыз."</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аутентификациядан өту қажет. Ол үшін саусақ ізін оқу сканерін түртіңіз."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Телефон қоңырауы бар"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Ұшақ режимі"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Жалғанды"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернет автоматты түрде қосылмайды."</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыс жоқ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Басқа қолжетімді желі жоқ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Қолжетімді желілер жоқ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Желі деректері"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Желіге қосылу үшін оны түртіңіз."</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 1d4b5153210b..183712baf50e 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ប្រើស្នាមម្រាមដៃ ដើម្បីបើក"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"តម្រូវឱ្យ​មាន​ការផ្ទៀងផ្ទាត់។ សូមចុច​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ ដើម្បី​ផ្ទៀងផ្ទាត់​។"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ការហៅទូរសព្ទ​ដែលកំពុង​ដំណើរការ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ពេលជិះ​យន្តហោះ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"បានភ្ជាប់"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"អ៊ីនធឺណិតនឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"មិនមាន​ការតភ្ជាប់ទេ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"មិន​មាន​បណ្ដាញផ្សេងទៀតដែល​អាច​ប្រើ​បានទេ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"មិន​មាន​បណ្ដាញដែល​អាច​ប្រើ​បានទេ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ព័ត៌មាន​លម្អិត​អំពីបណ្ដាញ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ចុចលើបណ្ដាញណាមួយ ដើម្បីភ្ជាប់"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិន​អាច​ភ្ជាប់​បណ្ដាញ​បានទេ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index cc6b15372e92..8eb874de17af 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ತೆರೆಯುವುದಕ್ಕಾಗಿ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಬಳಸಿ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ದೃಢೀಕರಣದ ಅವಶ್ಯಕತೆಯಿದೆ. ದೃಢೀಕರಿಸಲು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಫೋನ್ ಕರೆ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ಇಂಟರ್ನೆಟ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ಇತರ ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ವೈ‑ಫೈ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ನೆಟ್‌ವರ್ಕ್ ವಿವರಗಳು"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ಕನೆಕ್ಟ್ ಮಾಡಲು ಒಂದು ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 20a7c5f1545c..f1cd1903550f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"지문으로 열기"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"인증이 필요합니다. 지문 센서를 터치하여 인증하세요."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"진행 중인 전화 통화"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"비행기 모드"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"모바일 데이터"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"연결됨"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"인터넷에 자동으로 연결되지 않음"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"연결되지 않음"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"사용 가능한 다른 네트워크가 없음"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"사용 가능한 네트워크가 없음"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"네트워크 세부정보"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"연결하려면 네트워크를 탭하세요"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 34d799c2a25b..c0fce99cb241 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Манжаңыздын изи менен ачыңыз"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аныктыкты текшерүү талап кылынат. Аныктыгын текшерүү үчүн манжа изинин сенсоруна тийип коюңуз."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Учурдагы телефон чалуу"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Учак режими"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Туташты"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернет автоматтык түрдө туташпайт"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыш жок"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Башка тармактар жеткиликсиз"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Тармактар жеткиликтүү эмес"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Тармактын чоо-жайы"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Туташуу үчүн тармакты таптаңыз"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 8a47dddce06f..3c136b9e9196 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ໃຊ້ລາຍນິ້ວມືເພື່ອເປີດ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ຕ້ອງພິສູດຢືນຢັນ. ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມືເພື່ອພິສູດຢືນຢັນ."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ສາຍໂທອອກ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ໂໝດຢູ່ໃນຍົນ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ອິນເຕີເນັດມືຖື"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ຈະບໍ່ເຊື່ອມຕໍ່ອິນເຕີເນັດອັດຕະໂນມັດ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ບໍ່ມີການເຊື່ອມຕໍ່"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ບໍ່ມີເຄືອຂ່າຍອື່ນທີ່ສາມາດໃຊ້ໄດ້"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ບໍ່​ມ​ີ​ເຄືອ​ຂ່າຍ​ທີ່​ສາ​ມາດ​ໃຊ້​ໄດ້"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ລາຍລະອຽດເຄືອຂ່າຍ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ແຕະເຄືອຂ່າຍໃດໜຶ່ງເພື່ອເຊື່ອມຕໍ່"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 11d363ed59ef..39865f1e3644 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1173,4 +1173,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Naudokite kontrolinį kodą, kad atidarytumėte"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Reikia nustatyti tapatybę. Nustatykite tapatybę palietę kontrolinio kodo jutiklį."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Vykstantis telefono skambutis"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lėktuvo režimas"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Prisijungta"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Prie interneto nebus jungiamasi automatiškai"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nėra ryšio"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nėra kitų pasiekiamų tinklų"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nėra pasiekiamų tinklų"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Išsami tinklo informacija"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Palieskite tinklą, kad prisijungtumėte"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 63f85fb4c76d..860d338e9b8a 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1167,4 +1167,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Atvēršanai izmantojiet pirksta nospiedumu"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Nepieciešama autentifikācija. Pieskarieties pirksta nospieduma sensoram, lai veiktu autentificēšanu."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Notiekošs tālruņa zvans"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lidojuma režīms"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ir izveidots savienojums"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Interneta savienojums netiks izveidots automātiski"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nav savienojuma"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nav pieejams neviens cits tīkls"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nav pieejams neviens tīkls"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Dati par tīklu"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Pieskarieties tīklam, lai izveidotu savienojumu"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5392d52526f3..db02f9a976c5 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Користете отпечаток за да се отвори"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна е проверка. Допрете го сензорот за отпечаток за да автентицирате."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Тековен телефонски повик"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Авионски режим"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Поврзано"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Не може автоматски да се поврзе на интернет"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нема интернет-врска"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нема други достапни мрежи"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Нема достапни мрежи"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Детали за мрежата"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Допрете на мрежа за да се поврзете"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index cdb3a69144f6..1c3837f5c775 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"തുറക്കുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. പരിശോധിച്ചുറപ്പിക്കാൻ, വിരലടയാള സെൻസറിൽ സ്‌പർശിക്കുക."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"സജീവമായ ഫോൺ കോൾ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ഫ്ലൈറ്റ് മോഡ്"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"മൊബൈൽ ഡാറ്റ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"കണക്റ്റ് ചെയ്തു"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ഇന്റർനെറ്റ് സ്വയമേവ കണക്‌റ്റ് ചെയ്യില്ല"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"കണക്ഷനില്ല"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"മറ്റ് നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"വൈഫൈ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"നെറ്റ്‌വർക്ക് വിശദാംശങ്ങൾ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"കണക്‌റ്റ് ചെയ്യാൻ ഒരു നെറ്റ്‌വർക്കിൽ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്‌വർക്കുകൾ തിരയുന്നു…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്‌വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index d16e479dbc2a..4e04c61fa5d4 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Нээхийн тулд хурууны хээг ашиглана уу"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Баталгаажуулалт шаардлагатай. Баталгаажуулахын тулд хурууны хээ мэдрэгчид хүрнэ үү."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Үргэлжилж буй утасны дуудлага"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Нислэгийн горим"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобайл дата"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Холбогдсон"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернэт автоматаар холбогдохгүй"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Холболт алга"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Өөр боломжтой сүлжээ байхгүй байна"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Боломжтой сүлжээ байхгүй байна"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Сүлжээний дэлгэрэнгүй мэдээлэл"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Холбогдохын тулд сүлжээг товшино уу"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index bf27df9c63f9..b8dfaebdde83 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"उघडण्यासाठी फिंगरप्रिंट वापरा"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ऑथेंटिकेशन आवश्यक आहे. ऑथेंटिकेट करण्यासाठी फिंगरप्रिंट सेन्सरला स्पर्श करा."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फोन कॉल सुरू आहे"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"विमान मोड"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"इंटरनेट ऑटो-कनेक्ट होणार नाही"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"कोणतेही नेटवर्क उपलब्‍ध नाही"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"वाय-फाय"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्कचे तपशील"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"कनेक्ट करण्यासाठी नेटवर्कवर टॅप करा"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्‍ट करता आले नाही"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 1d16edcdc49c..72baf128a468 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan cap jari untuk membuka"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Pengesahan diperlukan. Sentuh penderia cap jari untuk pengesahan."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telefon yang sedang berjalan"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mod pesawat"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data mudah alih"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Disambungkan"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet tidak akan bersambung secara automatik"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tiada sambungan"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Tiada rangkaian lain yang tersedia"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Tiada rangkaian tersedia"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Butiran rangkaian"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ketik rangkaian untuk membuat sambungan"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index c92a45e8dbcc..d2db76a55424 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ဖွင့်ရန် လက်ဗွေကို သုံးပါ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"အထောက်အထားစိစစ်ခြင်း လိုအပ်သည်။ အထောက်အထားစိစစ်ရန် လက်ဗွေ အာရုံခံကိရိယာကို ထိပါ။"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"လက်ရှိ ဖုန်းခေါ်ဆိုမှု"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"လေယာဉ်ပျံမုဒ်"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"မိုဘိုင်းဒေတာ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ချိတ်ဆက်ထားသည်"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"အင်တာနက်က အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ချိတ်ဆက်မှုမရှိပါ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"အခြားကွန်ရက်များ မရှိပါ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ကွန်ရက်များ မရှိပါ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ကွန်ရက် အသေးစိတ်များ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ချိတ်ဆက်ရန် ကွန်ရက်ကို တို့ပါ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index a0db7794c4ea..7a42d3e3b24d 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Bruk fingeravtrykk for å åpne"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering kreves. Trykk på fingeravtrykkssensoren for å autentisere."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående telefonsamtale"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flymodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Tilkoblet"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internett kobles ikke til automatisk"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen tilkobling"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ingen andre nettverk er tilgjengelige"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ingen nettverk er tilgjengelige"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Nettverksdetaljer"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Trykk på et nettverk for å koble til"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index ee9b105d881d..253419ab75ab 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"फिंगरप्रिन्ट प्रयोग गरी खोल्नुहोस्"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि गर्नु पर्ने हुन्छ। पुष्टि गर्न फिंगरप्रिन्ट सेन्सर छुनुहोस्।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"जारी फोन कल"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"हवाइजहाज मोड"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"इन्टरनेटमा कनेक्ट गरिएको छ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"इन्टरनेट स्वतः कनेक्ट हुँदैन"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"इन्टरनेट छैन"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"अन्य नेटवर्क उपलब्ध छैनन्"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"कुनै पनि नेटवर्क उपलब्ध छैन"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्कसम्बन्धी विवरण"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"इन्टरनेटमा कनेक्ट गर्नका लागि कुनै नेटवर्कमा ट्याप गर्नुहोस्"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index b74892a39e18..f0cc2695dfee 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om te openen"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Verificatie vereist. Raak de vingerafdruksensor aan om de verificatie uit te voeren."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Actief telefoongesprek"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Vliegtuigmodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbonden"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet maakt niet automatisch verbinding"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen andere netwerken beschikbaar"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Geen netwerken beschikbaar"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netwerkgegevens"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tik op een netwerk om verbinding te maken"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 2ad54f7fd75a..fed17d2d4d4d 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ଖୋଲିବାକୁ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ପ୍ରମାଣୀକରଣ ଆବଶ୍ୟକ। ପ୍ରମାଣୀକରଣ କରିବାକୁ ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ଚାଲୁଥିବା ଫୋନ୍ କଲ୍"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ଏୟାରପ୍ଲେନ୍ ମୋଡ୍"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ସଂଯୋଗ କରାଯାଇଛି"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ଇଣ୍ଟରନେଟ୍ ସ୍ଵତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ସଂଯୋଗ ନାହିଁ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ଅନ୍ୟ କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ୱାଇ-ଫାଇ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ନେଟୱାର୍କ ବିବରଣୀ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ସଂଯୋଗ କରିବାକୁ ଏକ ନେଟୱାର୍କରେ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3f2e4810dbd2..fe6118e2d31e 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ਖੋਲ੍ਹਣ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ਪ੍ਰਮਾਣੀਕਰਨ ਲੋੜੀਂਦਾ ਹੈ। ਪ੍ਰਮਾਣਿਤ ਕਰਨ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ਜਾਰੀ ਫ਼ੋਨ ਕਾਲ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ਕਨੈਕਟ ਹੈ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ਇੰਟਰਨੈੱਟ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ਕੋਈ ਹੋਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ਕੋਈ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ਵਾਈ-ਫਾਈ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ਨੈੱਟਵਰਕ ਵੇਰਵੇ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ਕਨੈਕਟ ਕਰਨ ਲਈ ਕਿਸੇ ਨੈੱਟਵਰਕ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 990b461fe0f9..06757c709019 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1172,4 +1172,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"By otworzyć, użyj odcisku palca"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Wymagane uwierzytelnienie. Dotknij czytnika liniii papilarnych, by uwierzytelnić."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktywne połączenie"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Tryb samolotowy"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Połączono"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nie połączy się automatycznie z internetem"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Brak połączenia"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Brak innych dostępnych sieci"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Brak dostępnych sieci"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Szczegóły sieci"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Kliknij sieć, aby połączyć"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2755508efcf4..24015d8c8c3e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avião"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não será conectada automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nenhuma rede disponível"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque em uma rede para se conectar"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 16965af689cc..f1a4e03c796b 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilize a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação necessária. Toque no sensor de impressões digitais para autenticar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica em curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo de avião"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ligado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não estabelece ligação automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem ligação"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Sem redes disponíveis"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque numa rede para estabelecer ligação"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"A procurar redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Não foi possível estabelecer ligação à rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2755508efcf4..24015d8c8c3e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avião"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não será conectada automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nenhuma rede disponível"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque em uma rede para se conectar"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 346323685e83..c96e3bfc8b01 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1167,4 +1167,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosiți amprenta ca să deschideți"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atingeți senzorul de amprentă pentru a vă autentifica."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Apel telefonic în desfășurare"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modul Avion"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectat"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nu se conectează automat la internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nicio conexiune"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nu sunt disponibile alte rețele"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nicio rețea disponibilă"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalii despre rețea"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Atingeți o rețea pentru a vă conecta"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 8b0d70e4a8e4..ca476c1e9f7a 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1173,4 +1173,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Используйте отпечаток пальца для входа."</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Требуется аутентификация. Приложите палец к сканеру отпечатков."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущий вызов"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Режим полета"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Подключено"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Без автоподключения к интернету"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нет подключения к интернету"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нет других доступных сетей"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Нет доступных сетей"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Сведения о сети"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Выберите сеть, чтобы подключиться"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 4235d815d504..6cf9f9b0dd7a 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"විවෘත කිරීමට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"සත්‍යාපනය අවශ්‍යයි. සත්‍යාපනය කිරීමට ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ක්‍රියාත්මක වන දුරකථන ඇමතුම"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ගුවන් යානා ප්‍රකාරය"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ජංගම දත්ත"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"සම්බන්ධයි"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"අන්තර්ජාලය ස්වයංක්‍රියව සම්බන්ධ නොවනු ඇත"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"සම්බන්ධතාවයක් නැත"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ලබා ගත හැකි වෙනත් ජාල නැත"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ජාලය නොතිබේ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ජාල විස්තර"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"සම්බන්ධ වීමට ජාලයක් තට්ටු කරන්න"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 4967718196ef..0f91d813382e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1173,4 +1173,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorte odtlačkom prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vyžaduje sa overenie. Dotknite sa senzora odtlačkov prstov."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Prebiehajúci telefonický hovor"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Režim v lietadle"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilné dáta"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Pripojené"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet sa nepripojí automaticky"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bez pripojenia"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nie sú k dispozícii žiadne ďalšie siete"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nie sú k dispozícii žiadne siete"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Podrobnosti siete"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ak sa chcete pripojiť, klepnite na sieť"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index a553ff225e18..6bc27d0badcb 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1172,4 +1172,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Odprite s prstnim odtisom"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Zahtevano je preverjanje pristnosti. Za preverjanje pristnosti se dotaknite tipala prstnih odtisov."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Poteka klic"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Način za letalo"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podatkov v mobilnem omrežju"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Samodejna povezava z internetom ni mogoča."</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ni povezave"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nobeno drugo omrežje ni na voljo"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Na voljo ni nobeno omrežje"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Podatki o omrežju"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Za vzpostavitev povezave se dotaknite omrežja."</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index ad8e2659b612..cee829e229c5 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Përdor gjurmën e gishtit për ta hapur"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kërkohet vërtetimi. Prek sensorin e gjurmës së gishtit për t\'u vërtetuar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonatë në vazhdim"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modaliteti i aeroplanit"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Lidhur"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Interneti nuk do të lidhet automatikisht"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nuk ka lidhje"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nuk ofrohet asnjë rrjet tjetër"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nuk ofrohet asnjë rrjet"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detajet e rrjetit"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Trokit te një rrjet për t\'u lidhur"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 282d18ba77e0..0670a9cdee8f 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1166,4 +1166,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Отворите помоћу отиска прста"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна је потврда идентитета. Додирните сензор за отисак прста да бисте потврдили идентитет."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Актуелни телефонски позив"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Режим рада у авиону"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни подаци"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Повезано"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Аутоматско повезивање на интернет није могуће"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Веза није успостављена"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Није доступна ниједна друга мрежа"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Нема доступних мрежа"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Детаљи о мрежи"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Додирните мрежу да бисте се повезали"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c0588f666e39..b77205e73f57 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Öppna med fingeravtryck"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering krävs. Identifiera dig genom att trycka på fingeravtryckssensorn."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående samtal"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flygplansläge"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ansluten"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Du ansluts inte automatiskt till internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen anslutning"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Inga andra nätverk är tillgängliga"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Det finns inga tillgängliga nätverk"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Nätverksinformation"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tryck på ett nätverk för att ansluta"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index e603fc94ecbc..0ba5d05d351f 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Tumia alama ya kidole kufungua"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Uthibitishaji unahitajika. Gusa kitambua alama ya kidole ili uthibitishe."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Simu inayoendelea"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Hali ya ndegeni"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Imeunganishwa"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Intaneti haitaunganishwa kiotomatiki"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Hakuna muunganisho"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Hakuna mitandao mingine inayopatikana"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Hakuna mitandao inayopatikana"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Maelezo ya mtandao"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Gusa mtandao ili uunganishe"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index d1d6d0d0c334..4de2ab55ba5b 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"கைரேகையைப் பயன்படுத்தி திறந்திடுங்கள்"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"அங்கீகாரம் தேவை. கைரேகை சென்சாரைத் தொட்டு அங்கீகரியுங்கள்."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"செயலில் உள்ள மொபைல் அழைப்பு"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"விமானப் பயன்முறை"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"இணைக்கப்பட்டது"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"இணையத்துடன் தானாகவே இணைக்காது"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"இணைப்பு இல்லை"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"வேறு நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"வைஃபை"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"நெட்வொர்க் விவரங்கள்"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"இணையத்துடன் இணைய நெட்வொர்க்கைத் தட்டுங்கள்"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 409eced5a67d..5b15cff924d5 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"తెరవడానికి వేలిముద్రను ఉపయోగించండి"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ప్రామాణీకరణ అవసరం. ప్రామాణీకరించడానికి వేలిముద్ర సెన్సార్‌ను తాకండి."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ఫోన్ కాల్ జరుగుతోంది"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"విమానం మోడ్"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"కనెక్ట్ చేయబడింది"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ఇంటర్నెట్ ఆటోమెటిక్‌గా కనెక్ట్ అవ్వదు"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"కనెక్షన్ లేదు"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ఇతర నెట్‌వర్క్‌లేవీ అందుబాటులో లేవు"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"నెట్‌వర్క్‌లు అందుబాటులో లేవు"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"నెట్‌వర్క్ వివరాలు"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"కనెక్ట్ చేయడానికి నెట్‌వర్క్‌ను ట్యాప్ చేయండి"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్‌వర్క్‌ల కోసం సెర్చ్ చేస్తోంది…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్‌వర్క్‌కు కనెక్ట్ చేయడం విఫలమైంది"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6e0662693d4a..8b0a90f9f1e4 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ใช้ลายนิ้วมือเพื่อเปิด"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ต้องมีการตรวจสอบสิทธิ์ แตะเซ็นเซอร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"กำลังโทรอยู่"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"โหมดบนเครื่องบิน"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"อินเทอร์เน็ตมือถือ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"เชื่อมต่อแล้ว"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"อินเทอร์เน็ตจะไม่เชื่อมต่ออัตโนมัติ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ไม่มีการเชื่อมต่อ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ไม่มีเครือข่ายอื่นๆ ที่พร้อมใช้งาน"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ไม่มีเครือข่ายที่พร้อมใช้งาน"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"รายละเอียดเครือข่าย"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"แตะเครือข่ายเพื่อเชื่อมต่อ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9a83c227bf32..02195ab760a9 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gamitin ang fingerprint para buksan"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kailangan ng pag-authenticate. Pindutin ang sensor para sa fingerprint para mag-authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Kasalukuyang may tawag sa telepono"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Airplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Nakakonekta"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Hindi awtomatikong kokonekta ang Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Walang koneksyon"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Walang available na iba pang network"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Walang available na network"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Mga detalye ng network"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Mag-tap ng network para kumonekta"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index ac6ecd09b8d7..b7ad7cbbd08e 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmak için parmak izi kullanın"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kimlik doğrulaması gerekiyor. Kimlik doğrulaması için parmak izi sensörüne dokunun."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Devam eden telefon görüşmesi"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Uçak modu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Bağlı"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"İnternete otomatik olarak bağlanmaz"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yok"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Kullanılabilir başka ağ yok"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Kullanılabilir ağ yok"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Kablosuz"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Ağ bilgileri"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Bağlanmak için bir ağa dokunun"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 2e9f24029341..204414ca60a4 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1173,4 +1173,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Щоб відкрити, використайте відбиток пальця"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Пройдіть автентифікацію. Для цього торкніться сканера відбитків пальців."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Активний телефонний виклик"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Режим польоту"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Підключено"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Автоматичне інтернет-з’єднання вимкнено"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Немає з\'єднання"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Інші мережі недоступні"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Немає доступних мереж"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Деталі мережі"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Натисніть мережу, до якої потрібно підключитися"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ffcd9a750abf..06920440db19 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"کھولنے کے لیے فنگر پرنٹ کا استعمال کریں"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"توثیق مطلوب ہے۔ توثیق کرنے کے لیے فنگر پرنٹ سینسر کو ٹچ کریں۔"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"جاری فون کال"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ہوائی جہاز وضع"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g> / <xliff:g id="STATE">%1$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"انٹرنیٹ خود کار طور پر منسلک نہیں ہوگا"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"کوئی کنکشن نہیں"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"کوئی دوسرا نیٹ ورک دستیاب نہیں ہے"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"کوئی نیٹ ورکس دستیاب نہیں ہیں"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"نیٹ ورک کی تفصیلات"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"منسلک کرنے کے لیے نیٹ ورک پر تھپتھپائیں"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index b722acc87bef..fae90c8d5848 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ochish uchun barmoq izidan foydalaning"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Haqiqiylikni tekshirish talab etiladi. Autentifikatsiya uchun barmoq izi skaneriga tegining."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Joriy telefon chaqiruvi"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Parvoz rejimi"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ulandi"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet avtomatik ravishda ulanmaydi"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Internetga ulanmagansiz"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Boshqa tarmoqlar mavjud emas"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Hech qanday tarmoq mavjud emas"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Tarmoq tafsilotlari"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ulanish uchun tarmoq ustiga bosing"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Tarmoqlar qidirilmoqda…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Tarmoqqa ulanmadi"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index bc186362ad86..891d27d298a3 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Dùng vân tay để mở"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Bạn cần phải xác thực. Hãy chạm vào cảm biến vân tay để xác thực."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Đang gọi điện thoại"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Chế độ trên máy bay"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Đã kết nối"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Sẽ không tự động kết nối Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Không có kết nối mạng"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Không có mạng nào khác"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Không có mạng"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Thông tin chi tiết về mạng"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Nhấn vào một mạng để kết nối"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ae118bc22ead..f46cc6088192 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指纹即可打开"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要进行身份验证。请轻触指纹传感器以验证身份。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"正在进行通话"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"飞行模式"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"不会自动连接到互联网"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"无网络连接"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"没有其他可用网络"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"没有可用网络"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WLAN"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"网络详情"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"点按要连接的网络"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ea9f1fc1d472..9e498f61fe26 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1160,4 +1160,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。掂一下指紋感應器就可以驗證。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"飛行模式"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"不會自動連線至互聯網"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有連線"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網絡"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"沒有可用的網絡"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"網絡詳細資料"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"輕按網絡以連線"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 98b90cc13457..2cb78a7952e5 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。輕觸指紋感應器即可進行驗證。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"飛航模式"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"不會自動連上網際網路"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有網路連線"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網路"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"沒有可用的網路"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"網路詳細資料"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"輕觸要連線的網路"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6ced59776adf..a3c87f53e3c3 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1161,4 +1161,18 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Sebenzisa izigxivizo zeminwe ukuvula"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Ukufakazela ubuqiniso budingekile. Thinta inzwa yezigxivizo zeminwe ukuze uqinisekise."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ikholi yefoni eqhubekayo"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Imodi yendiza"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ixhunyiwe"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"I-inthanethi ngeke ixhumeke ngokuzenzakalelayo"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Alukho uxhumano"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Awekho amanye amanethiwekhi atholakalayo"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Awekho amanethiwekhi atholakalayo"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Imininingwane yenethiwekhi"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Thepha inethiwekhi ukuze uxhume"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2260d2175268..bbbaf65f861b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-->
-<resources>
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="ticker_background_color">#ff1d1d1d</drawable>
<drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
@@ -281,4 +281,16 @@
<color name="wallet_card_border">#33FFFFFF</color>
<color name="people_tile_background">@android:color/system_accent2_50</color>
+
+ <!-- Internet Dialog -->
+ <!-- Material next state on color-->
+ <color name="settingslib_state_on_color">?androidprv:attr/colorAccentPrimary</color>
+ <!-- Material next state off color-->
+ <color name="settingslib_state_off_color">?androidprv:attr/colorAccentSecondary</color>
+ <!-- Material next track on color-->
+ <color name="settingslib_track_on_color">?androidprv:attr/colorAccentPrimaryVariant</color>
+ <!-- Material next track off color-->
+ <color name="settingslib_track_off_color">?androidprv:attr/colorAccentSecondaryVariant</color>
+ <color name="connected_network_primary_color">#ff000000</color>
+ <color name="connected_network_tertiary_color">#808080</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f5d47ce9698c..d72286628c37 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -603,8 +603,6 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
- <!-- Whether global actions should show an informational message about changes in S -->
- <bool name="global_actions_show_change_info">false</bool>
<!-- Package name of the preferred system app to perform eSOS action -->
<string name="config_preferredEmergencySosPackage" translatable="false"></string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7485ef858620..22035f462c88 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -62,7 +62,9 @@
<item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item>
<dimen name="floating_rotation_button_diameter">40dp</dimen>
- <dimen name="floating_rotation_button_min_margin">4dp</dimen>
+ <dimen name="floating_rotation_button_min_margin">20dp</dimen>
+ <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
+ <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
@@ -402,8 +404,11 @@
<dimen name="status_bar_header_padding_bottom">48dp</dimen>
<!-- The height of the container that holds the battery and time in the quick settings header.
+ Preferred over using "@*android:dimen/quick_qs_offset_height" as system icons are not always
+ present in quick settings (e.g. in split shade) and it's useful to be able to override this
+ value in such cases.
-->
- <dimen name="qs_header_system_icons_area_height">48dp</dimen>
+ <dimen name="qs_header_system_icons_area_height">@*android:dimen/quick_qs_offset_height</dimen>
<!-- How far the quick-quick settings panel extends below the status bar -->
<dimen name="qs_quick_header_panel_height">128dp</dimen>
@@ -455,6 +460,10 @@
<!-- Width for the notification panel and related windows -->
<dimen name="match_parent">-1px</dimen>
+ <!-- Height of status bar in split shade mode - visible only on large screens -->
+ <dimen name="split_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
+ <dimen name="split_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
+
<!-- The top margin of the panel that holds the list of notifications. -->
<dimen name="notification_panel_margin_top">0dp</dimen>
@@ -623,6 +632,7 @@
<dimen name="qs_footer_icon_size">20dp</dimen>
<dimen name="qs_header_top_padding">15dp</dimen>
<dimen name="qs_header_bottom_padding">14dp</dimen>
+ <dimen name="qs_header_row_min_height">48dp</dimen>
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
@@ -1124,7 +1134,6 @@
<dimen name="global_actions_button_padding">38dp</dimen>
<dimen name="global_actions_corner_radius">28dp</dimen>
<dimen name="global_actions_lite_padding">24dp</dimen>
- <dimen name="global_actions_info_margin">32dp</dimen>
<!-- The maximum offset in either direction that elements are moved horizontally to prevent
burn-in on AOD. -->
@@ -1579,4 +1588,35 @@
<!-- The padding between the icon and the text. -->
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
+
+ <!-- Internet panel related dimensions -->
+ <dimen name="internet_dialog_list_margin">12dp</dimen>
+ <dimen name="internet_dialog_list_max_height">614dp</dimen>
+
+ <!-- Signal icon in internet dialog -->
+ <dimen name="signal_strength_icon_size">24dp</dimen>
+
+ <!-- Internet dialog related dimensions -->
+ <dimen name="internet_dialog_corner_radius">24dp</dimen>
+
+ <!-- Size of internet dialog -->
+ <dimen name="settingslib_switchbar_margin">16dp</dimen>
+ <!-- Minimum width of switch -->
+ <dimen name="settingslib_min_switch_width">48dp</dimen>
+ <!-- Size of layout margin left -->
+ <dimen name="settingslib_switchbar_padding_left">20dp</dimen>
+ <!-- Size of layout margin right -->
+ <dimen name="settingslib_switchbar_padding_right">20dp</dimen>
+ <!-- Radius of switch bar -->
+ <dimen name="settingslib_switch_bar_radius">24dp</dimen>
+ <!-- Margin of switch thumb -->
+ <dimen name="settingslib_switch_thumb_margin">4dp</dimen>
+ <!-- Size of switch thumb -->
+ <dimen name="settingslib_switch_thumb_size">16dp</dimen>
+ <!-- Width of switch track -->
+ <dimen name="settingslib_switch_track_width">48dp</dimen>
+ <!-- Height of switch track -->
+ <dimen name="settingslib_switch_track_height">24dp</dimen>
+ <!-- Radius of switch track -->
+ <dimen name="settingslib_switch_track_radius">31dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c4e9ff818df1..07ae5350a470 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2987,8 +2987,36 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
- <!-- Placeholder for string describing changes in global actions -->
- <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
- <!-- URL for more information about changes in global actions -->
- <string name="global_actions_change_url" translatable="false"></string>
+ <!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] -->
+ <string name="airplane_mode">Airplane mode</string>
+ <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
+ <string name="mobile_data_settings_title">Mobile data</string>
+ <!-- Provider Model: Summary text separator for preferences including a short description
+ (eg. "Connected / 5G"). [CHAR LIMIT=50] -->
+ <string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
+ <!-- Provider Model:
+ Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
+ <string name="mobile_data_connection_active">Connected</string>
+ <!-- Provider Model:
+ Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
+ <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
+ <!-- Provider Model:
+ Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
+ <string name="mobile_data_no_connection">No connection</string>
+ <!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
+ <string name="non_carrier_network_unavailable">No other networks available</string>
+ <!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] -->
+ <string name="all_network_unavailable">No networks available</string>
+ <!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
+ <string name="turn_on_wifi">Wi\u2011Fi</string>
+ <!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] -->
+ <string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string>
+ <!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] -->
+ <string name="tap_a_network_to_connect">Tap a network to connect</string>
+ <!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
+ <string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
+ <!-- Provider Model: Failure notification for connect -->
+ <string name="wifi_failed_connect_message">Failed to connect to network</string>
+ <!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
+ <string name="see_all_networks">See all</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 70ed81788e58..91d5d79220c2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -899,4 +899,43 @@
<item name="android:colorBackground">@android:color/system_neutral1_900</item>
<item name="android:itemBackground">@android:color/system_neutral1_800</item>
</style>
+
+ <style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod">
+ </style>
+
+ <style name="Widget.SliceView.Panel">
+ <item name="titleSize">16sp</item>
+ <item name="rowStyle">@style/SliceRow</item>
+ <item name="android:background">?android:attr/colorBackgroundFloating</item>
+ </style>
+
+ <style name="SliceRow">
+ <!-- 2dp start padding for the start icon -->
+ <item name="titleItemStartPadding">2dp</item>
+ <item name="titleItemEndPadding">0dp</item>
+
+ <!-- Padding between content and the start icon is 14dp -->
+ <item name="contentStartPadding">14dp</item>
+ <!-- Padding between content and end items is 16dp -->
+ <item name="contentEndPadding">16dp</item>
+
+ <!-- Both side margins of end item are 16dp -->
+ <item name="endItemStartPadding">16dp</item>
+ <item name="endItemEndPadding">16dp</item>
+
+ <!-- Both side margins of bottom divider are 12dp -->
+ <item name="bottomDividerStartPadding">12dp</item>
+ <item name="bottomDividerEndPadding">12dp</item>
+
+ <item name="actionDividerHeight">32dp</item>
+ </style>
+
+ <style name="Theme.SystemUI.Dialog.Internet">
+ <item name="android:windowBackground">@drawable/internet_dialog_background</item>
+ </style>
+
+ <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml
index ec9d8fd468a5..4bd6729227e8 100644
--- a/packages/SystemUI/shared/lint-baseline.xml
+++ b/packages/SystemUI/shared/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev">
<issue
id="NewApi"
@@ -300,6 +300,17 @@
<issue
id="NewApi"
+ message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
+ errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
+ line="192"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`"
errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -311,6 +322,17 @@
<issue
id="NewApi"
+ message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`"
+ errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
+ line="210"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`"
errorLine1=" leash.mSurfaceControl.release();"
errorLine2=" ~~~~~~~">
@@ -575,6 +597,17 @@
<issue
id="NewApi"
+ message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
+ errorLine1=" onTaskMovedToFront(taskInfo.taskId);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java"
+ line="70"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`"
errorLine1=" thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 277b2e31f7be..de9558ebca47 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -77,8 +77,17 @@ oneway interface IOverviewProxy {
void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17;
/**
- * Sent IME status changes
+ * Sent when suggested rotation button could be shown
*/
- void onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) = 18;
+ void onRotationProposal(int rotation, boolean isValid) = 18;
+
+ /**
+ * Sent when disable flags change
+ */
+ void disable(int displayId, int state1, int state2, boolean animate) = 19;
+
+ /**
+ * Sent when behavior changes. See WindowInsetsController#@Behavior
+ */
+ void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index f72245b9b252..11557ad87929 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -140,5 +140,8 @@ interface ISystemUiProxy {
/** Notifies that a swipe-up gesture has started */
oneway void notifySwipeUpGestureStarted() = 46;
- // Next id = 47
+ /** Notifies when taskbar status updated */
+ oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47;
+
+ // Next id = 48
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 7dffc2613956..e33985dc4288 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -16,13 +16,24 @@
package com.android.systemui.shared.recents.utilities;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.Message;
+import android.util.DisplayMetrics;
+import android.view.Surface;
/* Common code */
public class Utilities {
+ private static final float TABLET_MIN_DPS = 600;
+
/**
* Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
*/
@@ -31,6 +42,23 @@ public class Utilities {
h.sendMessageAtFrontOfQueue(msg);
}
+ public static boolean isRotationAnimationCCW(int from, int to) {
+ // All 180deg WM rotation animations are CCW, match that
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
+ return false; // Default
+ }
+
/** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
public static float computeContrastBetweenColors(int bg, int fg) {
float bgR = Color.red(bg) / 255f;
@@ -58,4 +86,52 @@ public class Utilities {
public static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
}
+
+ /**
+ * @return updated set of flags from InputMethodService based off {@param oldHints}
+ * Leaves original hints unmodified
+ */
+ public static int calculateBackDispositionHints(int oldHints, int backDisposition,
+ boolean imeShown, boolean showImeSwitcher) {
+ int hints = oldHints;
+ switch (backDisposition) {
+ case InputMethodService.BACK_DISPOSITION_DEFAULT:
+ case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
+ case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
+ if (imeShown) {
+ hints |= NAVIGATION_HINT_BACK_ALT;
+ } else {
+ hints &= ~NAVIGATION_HINT_BACK_ALT;
+ }
+ break;
+ case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
+ hints &= ~NAVIGATION_HINT_BACK_ALT;
+ break;
+ }
+ if (showImeSwitcher) {
+ hints |= NAVIGATION_HINT_IME_SHOWN;
+ } else {
+ hints &= ~NAVIGATION_HINT_IME_SHOWN;
+ }
+
+ return hints;
+ }
+
+ /** See {@link #isTablet(Configuration, Context)} */
+ public static boolean isTablet(Context context) {
+ Configuration newConfig = context.getResources().getConfiguration();
+ return isTablet(newConfig, context);
+ }
+
+ /**
+ * @return whether or not {@param newConfig} represents that of a large screen device or not
+ */
+ public static boolean isTablet(Configuration newConfig, Context context) {
+ float density = Resources.getSystem().getDisplayMetrics().density;
+ int size = Math.min((int) (density * newConfig.screenWidthDp),
+ (int) (density* newConfig.screenHeightDp));
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ return (size / densityRatio) >= TABLET_MIN_DPS;
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
new file mode 100644
index 000000000000..5581a1c90527
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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.shared.recents.utilities;
+
+import android.view.View;
+
+/**
+ * Shows view ripples by toggling the provided Views "pressed" state.
+ * Ripples 4 times.
+ */
+public class ViewRippler {
+ private static final int RIPPLE_OFFSET_MS = 50;
+ private static final int RIPPLE_INTERVAL_MS = 2000;
+ private View mRoot;
+
+ public void start(View root) {
+ stop(); // Stop any pending ripple animations
+
+ mRoot = root;
+
+ // Schedule pending ripples, offset the 1st to avoid problems with visibility change
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
+ }
+
+ public void stop() {
+ if (mRoot != null) mRoot.removeCallbacks(mRipple);
+ }
+
+ private final Runnable mRipple = new Runnable() {
+ @Override
+ public void run() { // Cause the ripple to fire via false presses
+ if (!mRoot.isAttachedToWindow()) return;
+ mRoot.setPressed(true /* pressed */);
+ mRoot.setPressed(false /* pressed */);
+ }
+ };
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index c468e416f8a5..4663a9afcd3d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -112,6 +112,10 @@ public class QuickStepContract {
public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
// The window magnification is overlapped with system gesture insets at the bottom.
public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
+ // ImeSwitcher is showing
+ public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
+ // Device dozing/AOD state
+ public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -133,7 +137,9 @@ public class QuickStepContract {
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
SYSUI_STATE_IME_SHOWING,
- SYSUI_STATE_MAGNIFICATION_OVERLAP
+ SYSUI_STATE_MAGNIFICATION_OVERLAP,
+ SYSUI_STATE_IME_SWITCHER_SHOWING,
+ SYSUI_STATE_DEVICE_DOZING
})
public @interface SystemUiStateFlags {}
@@ -162,6 +168,8 @@ public class QuickStepContract {
? "allow_gesture" : "");
str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
+ str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : "");
+ str.add((flags & SYSUI_STATE_DEVICE_DOZING) != 0 ? "device_dozing" : "");
return str.toString();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index ee55bf0aa8b7..025d7ef48096 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -69,7 +69,8 @@ public class RemoteAnimationAdapterCompat {
return mRemoteTransition;
}
- private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
+ /** Wraps a RemoteAnimationRunnerCompat in an IRemoteAnimationRunner. */
+ public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
@@ -260,7 +261,7 @@ public class RemoteAnimationAdapterCompat {
t.remove(leashMap.valueAt(i));
}
t.apply();
- finishCallback.onTransitionFinished(null /* wct */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 653d73020c4f..3eea99a46245 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -17,14 +17,18 @@
package com.android.systemui.shared.system;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcelable;
@@ -73,7 +77,7 @@ public class RemoteTransitionCompat implements Parcelable {
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call transition finished callback", e);
}
@@ -87,7 +91,7 @@ public class RemoteTransitionCompat implements Parcelable {
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call transition finished callback", e);
}
@@ -119,14 +123,20 @@ public class RemoteTransitionCompat implements Parcelable {
// This transition is for opening recents, so recents is on-top. We want to draw
// the current going-away task on top of recents, though, so move it to front
WindowContainerToken pausingTask = null;
+ WindowContainerToken pipTask = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
t.setLayer(leashMap.get(change.getLeash()),
info.getChanges().size() * 3 - i);
- if (change.getTaskInfo() != null) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null) {
pausingTask = change.getTaskInfo().token;
}
+ if (taskInfo.pictureInPictureParams != null
+ && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
+ pipTask = change.getTaskInfo().token;
+ }
}
}
// Also make all the wallpapers opaque since we want the visible from the start
@@ -134,8 +144,8 @@ public class RemoteTransitionCompat implements Parcelable {
t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
}
t.apply();
- mRecentsSession.setup(controller, info, finishedCallback, pausingTask,
- leashMap);
+ mRecentsSession.setup(controller, info, finishedCallback, pausingTask, pipTask,
+ leashMap, mToken);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -147,7 +157,7 @@ public class RemoteTransitionCompat implements Parcelable {
if (!mergeTarget.equals(mToken)) return;
if (!mRecentsSession.merge(info, t, recents)) return;
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Error merging transition.", e);
}
@@ -161,9 +171,13 @@ public class RemoteTransitionCompat implements Parcelable {
mFilter = new TransitionFilter();
}
mFilter.mRequirements =
- new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+ new TransitionFilter.Requirement()};
mFilter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
mFilter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ mFilter.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+ mFilter.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
+ mFilter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
}
/**
@@ -175,13 +189,17 @@ public class RemoteTransitionCompat implements Parcelable {
private RecentsAnimationControllerCompat mWrapped = null;
private IRemoteTransitionFinishedCallback mFinishCB = null;
private WindowContainerToken mPausingTask = null;
+ private WindowContainerToken mPipTask = null;
private TransitionInfo mInfo = null;
private SurfaceControl mOpeningLeash = null;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
+ private PictureInPictureSurfaceTransaction mPipTransaction = null;
+ private IBinder mTransition = null;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
- ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+ WindowContainerToken pipTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
+ IBinder transition) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -190,7 +208,9 @@ public class RemoteTransitionCompat implements Parcelable {
mInfo = info;
mFinishCB = finishCB;
mPausingTask = pausingTask;
+ mPipTask = pipTask;
mLeashMap = leashMap;
+ mTransition = transition;
}
@SuppressLint("NewApi")
@@ -247,6 +267,7 @@ public class RemoteTransitionCompat implements Parcelable {
@Override public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
+ mPipTransaction = finishTransaction;
if (mWrapped != null) {
mWrapped.setFinishTaskTransaction(taskId, finishTransaction, overlay);
}
@@ -263,10 +284,13 @@ public class RemoteTransitionCompat implements Parcelable {
try {
if (!toHome && mPausingTask != null && mOpeningLeash == null) {
// The gesture went back to opening the app rather than continuing with
- // recents, so end the transition by moving the app back to the top.
+ // recents, so end the transition by moving the app back to the top (and also
+ // re-showing it's task).
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mPausingTask, true /* onTop */);
- mFinishCB.onTransitionFinished(wct);
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(mInfo.getChange(mPausingTask).getLeash());
+ mFinishCB.onTransitionFinished(wct, t);
} else {
if (mOpeningLeash != null) {
// TODO: the launcher animation should handle this
@@ -275,7 +299,18 @@ public class RemoteTransitionCompat implements Parcelable {
t.setAlpha(mOpeningLeash, 1.f);
t.apply();
}
- mFinishCB.onTransitionFinished(null /* wct */);
+ if (mPipTask != null && mPipTransaction != null) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(mInfo.getChange(mPipTask).getLeash());
+ PictureInPictureSurfaceTransaction.apply(mPipTransaction,
+ mInfo.getChange(mPipTask).getLeash(), t);
+ mPipTask = null;
+ mPipTransaction = null;
+ mFinishCB.onTransitionFinished(null /* wct */, t);
+ } else {
+ mFinishCB.onTransitionFinished(null /* wct */, null /* sct */);
+ }
+
}
} catch (RemoteException e) {
Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
@@ -298,6 +333,7 @@ public class RemoteTransitionCompat implements Parcelable {
mInfo = null;
mOpeningLeash = null;
mLeashMap = null;
+ mTransition = null;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
@@ -318,6 +354,23 @@ public class RemoteTransitionCompat implements Parcelable {
@Override public boolean removeTask(int taskId) {
return mWrapped != null ? mWrapped.removeTask(taskId) : false;
}
+
+ /**
+ * @see IRecentsAnimationController#detachNavigationBarFromApp
+ */
+ @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+ try {
+ ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to detach the navigation bar from app", e);
+ }
+ }
+
+ /**
+ * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+ */
+ @Override public void animateNavigationBarToApp(long duration) {
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
index 2d01d6af63c5..e49d9f92267e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
@@ -16,6 +16,4 @@
package com.android.systemui.shared.system.smartspace;
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-
-parcelable SmartspaceState; \ No newline at end of file
+parcelable SmartspaceState;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index a5b25097a56e..8f14cd858f1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,6 +14,9 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
@@ -22,6 +25,8 @@ import com.android.systemui.plugins.ClockPlugin;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.TimeZone;
@@ -37,6 +42,13 @@ public class KeyguardClockSwitch extends RelativeLayout {
private static final long CLOCK_IN_MILLIS = 200;
private static final long SMARTSPACE_MOVE_MILLIS = 350;
+ @IntDef({LARGE, SMALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ClockSize { }
+
+ public static final int LARGE = 0;
+ public static final int SMALL = 1;
+
/**
* Optional/alternative clock injected via plugin.
*/
@@ -65,13 +77,13 @@ public class KeyguardClockSwitch extends RelativeLayout {
private float mDarkAmount;
/**
- * Boolean value indicating if notifications are visible on lock screen. Use null to signify
- * it is uninitialized.
+ * Indicates which clock is currently displayed - should be one of {@link ClockSize}.
+ * Use null to signify it is uninitialized.
*/
- private Boolean mHasVisibleNotifications = null;
+ @ClockSize private Integer mDisplayedClockSize = null;
- private AnimatorSet mClockInAnim = null;
- private AnimatorSet mClockOutAnim = null;
+ @VisibleForTesting AnimatorSet mClockInAnim = null;
+ @VisibleForTesting AnimatorSet mClockOutAnim = null;
private ObjectAnimator mSmartspaceAnim = null;
/**
@@ -264,19 +276,17 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
/**
- * Based upon whether notifications are showing or not, display/hide the large clock and
- * the smaller version.
+ * Display the desired clock and hide the other one
+ *
+ * @return true if desired clock appeared and false if it was already visible
*/
- boolean willSwitchToLargeClock(boolean hasVisibleNotifications) {
- if (mHasVisibleNotifications != null
- && hasVisibleNotifications == mHasVisibleNotifications) {
+ boolean switchToClock(@ClockSize int clockSize) {
+ if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
- boolean useLargeClock = !hasVisibleNotifications;
- animateClockChange(useLargeClock);
-
- mHasVisibleNotifications = hasVisibleNotifications;
- return useLargeClock;
+ animateClockChange(clockSize == LARGE);
+ mDisplayedClockSize = clockSize;
+ return true;
}
public Paint getPaint() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 632919ae51e4..9e456cf55500 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,6 +19,8 @@ package com.android.keyguard;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+
import android.app.WallpaperManager;
import android.text.TextUtils;
import android.view.View;
@@ -245,10 +247,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Set which clock should be displayed on the keyguard. The other one will be automatically
+ * hidden.
*/
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
+ public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+ boolean appeared = mView.switchToClock(clockSize);
+ if (appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ca4d73b6de5d..840e8c8cba58 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -42,7 +42,6 @@ import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
import android.widget.FrameLayout;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -104,7 +103,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
private boolean mIsSecurityViewLeftAligned = true;
private boolean mOneHandedMode = false;
- private SecurityMode mSecurityMode = SecurityMode.Invalid;
private ViewPropertyAnimator mRunningOneHandedAnimator;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -248,66 +246,47 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
- mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
-
- updateLayoutForSecurityMode(securityMode);
}
- void updateLayoutForSecurityMode(SecurityMode securityMode) {
- mSecurityMode = securityMode;
- mOneHandedMode = canUseOneHandedBouncer();
-
- if (mOneHandedMode) {
- mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
- }
-
+ /**
+ * Sets whether this security container is in one handed mode. If so, it will measure its
+ * child SecurityViewFlipper in one half of the screen, and move it when tapping on the opposite
+ * side of the screen.
+ */
+ public void setOneHandedMode(boolean oneHandedMode) {
+ mOneHandedMode = oneHandedMode;
updateSecurityViewGravity();
updateSecurityViewLocation(false);
}
- /** Update keyguard position based on a tapped X coordinate. */
- public void updateKeyguardPosition(float x) {
- if (mOneHandedMode) {
- moveBouncerForXCoordinate(x, /* animate= */false);
- }
+ /** Returns whether this security container is in one-handed mode. */
+ public boolean isOneHandedMode() {
+ return mOneHandedMode;
}
- /** Return whether the one-handed keyguard should be enabled. */
- private boolean canUseOneHandedBouncer() {
- // Is it enabled?
- if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
- return false;
- }
-
- if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
- return false;
- }
-
- return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ /**
+ * When in one-handed mode, sets if the inner SecurityViewFlipper should be aligned to the
+ * left-hand side of the screen or not, and whether to animate when moving between the two.
+ */
+ public void setOneHandedModeLeftAligned(boolean leftAligned, boolean animate) {
+ mIsSecurityViewLeftAligned = leftAligned;
+ updateSecurityViewLocation(animate);
}
- /** Read whether the one-handed keyguard should be on the left/right from settings. */
- private boolean isOneHandedKeyguardLeftAligned(Context context) {
- try {
- return Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
- == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
- } catch (Settings.SettingNotFoundException ex) {
- return true;
- }
+ /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
+ public boolean isOneHandedModeLeftAligned() {
+ return mIsSecurityViewLeftAligned;
}
private void updateSecurityViewGravity() {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
+ if (mSecurityViewFlipper == null) {
return;
}
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mSecurityViewFlipper.getLayoutParams();
if (mOneHandedMode) {
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
@@ -315,7 +294,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
lp.gravity = Gravity.CENTER_HORIZONTAL;
}
- securityView.setLayoutParams(lp);
+ mSecurityViewFlipper.setLayoutParams(lp);
}
/**
@@ -324,14 +303,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
* by the security view .
*/
private void updateSecurityViewLocation(boolean animate) {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
+ if (mSecurityViewFlipper == null) {
return;
}
if (!mOneHandedMode) {
- securityView.setTranslationX(0);
+ mSecurityViewFlipper.setTranslationX(0);
return;
}
@@ -343,7 +320,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
if (animate) {
- mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator =
+ mSecurityViewFlipper.animate().translationX(targetTranslation);
mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
@Override
@@ -355,27 +333,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
mRunningOneHandedAnimator.start();
} else {
- securityView.setTranslationX(targetTranslation);
+ mSecurityViewFlipper.setTranslationX(targetTranslation);
}
}
- @Nullable
- private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
-
- if (isKeyguardSecurityView(child)) {
- return (KeyguardSecurityViewFlipper) child;
- }
- }
-
- return null;
- }
-
- private boolean isKeyguardSecurityView(View view) {
- return view instanceof KeyguardSecurityViewFlipper;
- }
-
public void onPause() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
@@ -635,7 +596,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
- if (mOneHandedMode && isKeyguardSecurityView(view)) {
+ if (mOneHandedMode && view == mSecurityViewFlipper) {
measureChildWithMargins(view, halfWidthMeasureSpec, 0,
heightMeasureSpec, 0);
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fde8213de0c6..cb5c6c3a0090 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -32,6 +32,7 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.metrics.LogMaker;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
@@ -49,6 +50,8 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -74,12 +77,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final SecurityCallback mSecurityCallback;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
- private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+ @VisibleForTesting
+ final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
private MotionEvent mTouchDown;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -91,6 +96,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
// Do just a bit of our own falsing. People should only be tapping on the input, not
// swiping.
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // If we're in one handed mode, the user can tap on the opposite side of the screen
+ // to move the bouncer across. In that case, inhibit the falsing (otherwise the taps
+ // to move the bouncer to each screen side can end up closing it instead).
+ if (mView.isOneHandedMode()) {
+ if ((mView.isOneHandedModeLeftAligned() && ev.getX() > mView.getWidth() / 2f)
+ || (!mView.isOneHandedModeLeftAligned()
+ && ev.getX() <= mView.getWidth() / 2f)) {
+ mFalsingCollector.avoidGesture();
+ }
+ }
+
if (mTouchDown != null) {
mTouchDown.recycle();
mTouchDown = null;
@@ -202,7 +218,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
KeyguardStateController keyguardStateController,
SecurityCallback securityCallback,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -216,6 +233,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mKeyguardSecurityCallback);
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
+ mFalsingCollector = falsingCollector;
}
@Override
@@ -440,13 +458,49 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
- mView.updateLayoutForSecurityMode(securityMode);
+ configureOneHandedMode();
}
mSecurityCallback.onSecurityModeChanged(
securityMode, newView != null && newView.needsInput());
}
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned() {
+ try {
+ return Settings.Global.getInt(mView.getContext().getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mCurrentSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ private void configureOneHandedMode() {
+ boolean oneHandedMode = canUseOneHandedBouncer();
+
+ mView.setOneHandedMode(oneHandedMode);
+
+ if (oneHandedMode) {
+ mView.setOneHandedModeLeftAligned(
+ isOneHandedKeyguardLeftAligned(), /* animate= */false);
+ }
+ }
+
public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
// +1 for this time
final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
@@ -511,13 +565,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
int newOrientation = getResources().getConfiguration().orientation;
if (newOrientation != mLastOrientation) {
mLastOrientation = newOrientation;
- mView.updateLayoutForSecurityMode(mCurrentSecurityMode);
+ configureOneHandedMode();
}
}
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- mView.updateKeyguardPosition(x);
+ if (mView.isOneHandedMode()) {
+ mView.setOneHandedModeLeftAligned(x <= mView.getWidth() / 2f, false);
+ }
}
static class Factory {
@@ -533,6 +589,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -545,7 +602,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
UiEventLogger uiEventLogger,
KeyguardStateController keyguardStateController,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -556,6 +614,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mKeyguardStateController = keyguardStateController;
mSecurityViewFlipperController = securityViewFlipperController;
mConfigurationController = configurationController;
+ mFalsingCollector = falsingCollector;
}
public KeyguardSecurityContainerController create(
@@ -564,7 +623,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
- mConfigurationController);
+ mConfigurationController, mFalsingCollector);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 72e502816534..8bf8e0926095 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -19,6 +19,7 @@ package com.android.keyguard;
import android.graphics.Rect;
import android.util.Slog;
+import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -116,10 +117,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Set which clock should be displayed on the keyguard. The other one will be automatically
+ * hidden.
*/
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications);
+ public void displayClock(@ClockSize int clockSize) {
+ mKeyguardClockSwitchController.displayClock(clockSize);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e6e2ac980889..672e2e629620 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2779,6 +2779,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
updateBiometricListeningState();
}
+ /** Notifies that the occluded state changed. */
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ Assert.isMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")");
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onKeyguardOccludedChanged(occluded);
+ }
+ }
+ }
+
/**
* Handle {@link #MSG_KEYGUARD_RESET}
*/
@@ -2961,6 +2975,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo();
callback.onClockVisibilityChanged();
+ callback.onKeyguardOccludedChanged(mKeyguardOccluded);
callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
callback.onTelephonyCapable(mTelephonyCapable);
callback.onLockScreenModeChanged(mLockScreenMode);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 9849a7efe837..6aa7aaa4d488 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -88,6 +88,12 @@ public class KeyguardUpdateMonitorCallback {
*/
public void onKeyguardVisibilityChanged(boolean showing) { }
+ /**
+ * Called when the keyguard occluded state changes.
+ * @param occluded Indicates if the keyguard is now occluded.
+ */
+ public void onKeyguardOccludedChanged(boolean occluded) { }
+
public void onKeyguardVisibilityChangedRaw(boolean showing) {
final long now = SystemClock.elapsedRealtime();
if (showing == mShowing
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 62d5a458d51d..06fbe842eb85 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -39,113 +39,114 @@ import dagger.Lazy;
@SysUISingleton
public class ActivityStarterDelegate implements ActivityStarter {
- private Optional<Lazy<StatusBar>> mActualStarter;
+ private Lazy<Optional<StatusBar>> mActualStarterOptionalLazy;
@Inject
- public ActivityStarterDelegate(Optional<Lazy<StatusBar>> statusBar) {
- mActualStarter = statusBar;
+ public ActivityStarterDelegate(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+ mActualStarterOptionalLazy = statusBarOptionalLazy;
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(intent));
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
- intentSentUiThreadCallback));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(
+ intent, intentSentUiThreadCallback));
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback, View associatedView) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
- intentSentUiThreadCallback, associatedView));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(
+ intent, intentSentUiThreadCallback, associatedView));
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback,
ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
- intentSentUiThreadCallback, animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(
+ intent, intentSentUiThreadCallback, animationController));
}
@Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
int flags) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade,
- flags));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, onlyProvisioned, dismissShade, flags));
}
@Override
public void startActivity(Intent intent, boolean dismissShade) {
- mActualStarter.ifPresent(starter -> starter.get().startActivity(intent, dismissShade));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade));
}
@Override
public void startActivity(Intent intent, boolean dismissShade,
@Nullable ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, dismissShade, animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade, animationController));
}
@Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, onlyProvisioned, dismissShade));
}
@Override
public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, dismissShade, callback));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade, callback));
}
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay) {
- mActualStarter.ifPresent(
- starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(intent, delay));
}
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(
- starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay,
- animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(
+ intent, delay, animationController));
}
@Override
public void postStartActivityDismissingKeyguard(PendingIntent intent) {
- mActualStarter.ifPresent(
- starter -> starter.get().postStartActivityDismissingKeyguard(intent));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(intent));
}
@Override
public void postStartActivityDismissingKeyguard(PendingIntent intent,
ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(starter ->
- starter.get().postStartActivityDismissingKeyguard(intent, animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(
+ intent, animationController));
}
@Override
public void postQSRunnableDismissingKeyguard(Runnable runnable) {
- mActualStarter.ifPresent(
- starter -> starter.get().postQSRunnableDismissingKeyguard(runnable));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postQSRunnableDismissingKeyguard(runnable));
}
@Override
public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancel,
boolean afterKeyguardGone) {
- mActualStarter.ifPresent(starter -> starter.get().dismissKeyguardThenExecute(action, cancel,
- afterKeyguardGone));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.dismissKeyguardThenExecute(action, cancel, afterKeyguardGone));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 54e78cf0ab74..6fc2e41782a0 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -192,6 +192,7 @@ public class BatteryMeterView extends LinearLayout implements
* 0 - No preference
* 1 - Force on
* 2 - Force off
+ * 3 - Estimate
* @param mode desired mode (none, on, off)
*/
public void setPercentShowMode(@BatteryPercentMode int mode) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 104d711f46fb..65c3847be4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -65,6 +65,7 @@ import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
@@ -99,7 +100,6 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -350,7 +350,6 @@ public class Dependency {
@Inject Lazy<IWallpaperManager> mWallpaperManager;
@Inject Lazy<CommandQueue> mCommandQueue;
@Inject Lazy<Recents> mRecents;
- @Inject Lazy<StatusBar> mStatusBar;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
@Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
@@ -361,6 +360,7 @@ public class Dependency {
@Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
@Inject Lazy<EdgeBackGestureHandler> mEdgeBackGestureHandler;
@Inject Lazy<UiEventLogger> mUiEventLogger;
+ @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
@Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject
@@ -554,7 +554,6 @@ public class Dependency {
mProviders.put(IWallpaperManager.class, mWallpaperManager::get);
mProviders.put(CommandQueue.class, mCommandQueue::get);
mProviders.put(Recents.class, mRecents::get);
- mProviders.put(StatusBar.class, mStatusBar::get);
mProviders.put(ProtoTracer.class, mProtoTracer::get);
mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get);
@@ -575,6 +574,7 @@ public class Dependency {
mSystemStatusAnimationSchedulerLazy::get);
mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
mProviders.put(EdgeBackGestureHandler.class, mEdgeBackGestureHandler::get);
+ mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 0ae89bc10290..1d355f22edd5 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -16,25 +16,24 @@
package com.android.systemui;
-import android.app.ActivityManager;
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.QSUserSwitcherEvent;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.util.settings.SecureSettings;
/**
* Manages notification when a guest session is resumed.
@@ -43,16 +42,23 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
private static final String TAG = "GuestResumeSessionReceiver";
- private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
+ @VisibleForTesting
+ public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
- private Dialog mNewSessionDialog;
+ @VisibleForTesting
+ public AlertDialog mNewSessionDialog;
+ private final UserTracker mUserTracker;
private final UserSwitcherController mUserSwitcherController;
private final UiEventLogger mUiEventLogger;
+ private final SecureSettings mSecureSettings;
public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController,
- UiEventLogger uiEventLogger) {
+ UserTracker userTracker, UiEventLogger uiEventLogger,
+ SecureSettings secureSettings) {
mUserSwitcherController = userSwitcherController;
+ mUserTracker = userTracker;
mUiEventLogger = uiEventLogger;
+ mSecureSettings = secureSettings;
}
/**
@@ -78,26 +84,19 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
return;
}
- UserInfo currentUser;
- try {
- currentUser = ActivityManager.getService().getCurrentUser();
- } catch (RemoteException e) {
- return;
- }
+ UserInfo currentUser = mUserTracker.getUserInfo();
if (!currentUser.isGuest()) {
return;
}
- ContentResolver cr = context.getContentResolver();
- int notFirstLogin = Settings.System.getIntForUser(
- cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
+ int notFirstLogin = mSecureSettings.getIntForUser(
+ SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
if (notFirstLogin != 0) {
mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController,
- mUiEventLogger, userId);
+ mUserTracker, mUiEventLogger, userId);
mNewSessionDialog.show();
} else {
- Settings.System.putIntForUser(
- cr, SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
}
}
}
@@ -109,18 +108,27 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
}
}
- private static class ResetSessionDialog extends SystemUIDialog implements
+ /**
+ * Dialog shown when user when asking for confirmation before deleting guest user.
+ */
+ @VisibleForTesting
+ public static class ResetSessionDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
- private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
- private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+ @VisibleForTesting
+ public static final int BUTTON_WIPE = BUTTON_NEGATIVE;
+ @VisibleForTesting
+ public static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
private final UserSwitcherController mUserSwitcherController;
private final UiEventLogger mUiEventLogger;
private final int mUserId;
- ResetSessionDialog(Context context, UserSwitcherController userSwitcherController,
- UiEventLogger uiEventLogger, int userId) {
+ ResetSessionDialog(Context context,
+ UserSwitcherController userSwitcherController,
+ UserTracker userTracker,
+ UiEventLogger uiEventLogger,
+ int userId) {
super(context);
setTitle(context.getString(R.string.guest_wipe_session_title));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 17178fa8e606..e521c90961fb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -310,7 +310,8 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
}
void onConfigurationChanged(int configDiff) {
- if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+ if ((configDiff & (ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE))
+ != 0) {
final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
mDraggableWindowBounds.set(getDraggableWindowBounds());
// Keep the Y position with the same height ratio before the window bounds and
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index ca2c034c5d32..a2005d2bbe01 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.util.Assert;
import java.util.Locale;
+import java.util.Optional;
import javax.inject.Inject;
@@ -142,7 +143,7 @@ public class SystemActions extends SystemUI {
private final Recents mRecents;
private Locale mLocale;
private final AccessibilityManager mA11yManager;
- private final Lazy<StatusBar> mStatusBar;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final NotificationShadeWindowController mNotificationShadeController;
private final StatusBarWindowCallback mNotificationShadeCallback;
private boolean mDismissNotificationShadeActionRegistered;
@@ -150,7 +151,7 @@ public class SystemActions extends SystemUI {
@Inject
public SystemActions(Context context,
NotificationShadeWindowController notificationShadeController,
- Lazy<StatusBar> statusBar,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Recents recents) {
super(context);
mRecents = recents;
@@ -161,9 +162,9 @@ public class SystemActions extends SystemUI {
mNotificationShadeController = notificationShadeController;
// Saving in instance variable since to prevent GC since
// NotificationShadeWindowController.registerCallback() only keeps weak references.
- mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) ->
+ mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) ->
registerOrUnregisterDismissNotificationShadeAction();
- mStatusBar = statusBar;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
@Override
@@ -242,8 +243,9 @@ public class SystemActions extends SystemUI {
// Saving state in instance variable since this callback is called quite often to avoid
// binder calls
- StatusBar statusBar = mStatusBar.get();
- if (statusBar.isPanelExpanded() && !statusBar.isKeyguardShowing()) {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (statusBarOptional.map(StatusBar::isPanelExpanded).orElse(false)
+ && !statusBarOptional.get().isKeyguardShowing()) {
if (!mDismissNotificationShadeActionRegistered) {
mA11yManager.registerSystemAction(
createRemoteAction(
@@ -372,11 +374,12 @@ public class SystemActions extends SystemUI {
}
private void handleNotifications() {
- mStatusBar.get().animateExpandNotificationsPanel();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::animateExpandNotificationsPanel);
}
private void handleQuickSettings() {
- mStatusBar.get().animateExpandSettingsPanel(null);
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.animateExpandSettingsPanel(null));
}
private void handlePowerDialog() {
@@ -425,7 +428,9 @@ public class SystemActions extends SystemUI {
}
private void handleAccessibilityDismissNotificationShade() {
- mStatusBar.get().animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, false /* force */);
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
}
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index cee395bd9a6a..32813479dc0a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -91,7 +91,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
final WindowMagnificationController controller = new WindowMagnificationController(
- mContext,
+ windowContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
return new WindowMagnificationAnimationController(windowContext, controller);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 717c7156f917..a51e3fcfd50b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -28,6 +28,7 @@ import android.annotation.Nullable;
import android.annotation.UiContext;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -35,6 +36,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
@@ -76,6 +78,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
private static final String TAG = "WindowMagnificationController";
+ @SuppressWarnings("isloggabletaglength")
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Build.IS_DEBUGGABLE;
// Delay to avoid updating state description too frequently.
private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100;
// It should be consistent with the value defined in WindowMagnificationGestureHandler.
@@ -158,14 +162,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mRotation = display.getRotation();
mWm = context.getSystemService(WindowManager.class);
- mWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
+ mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
mResources = mContext.getResources();
mScale = mResources.getInteger(R.integer.magnification_default_scale);
mBounceEffectDuration = mResources.getInteger(
com.android.internal.R.integer.config_shortAnimTime);
updateDimensions();
- setInitialStartBounds();
+ setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2,
+ mWindowBounds.height() / 2);
computeBounceAnimationScale();
mMirrorWindowControl = mirrorWindowControl;
@@ -286,18 +291,59 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* @param configDiff a bit mask of the differences between the configurations
*/
void onConfigurationChanged(int configDiff) {
+ if (DEBUG) {
+ Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString(
+ configDiff));
+ }
+ if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+ onRotate();
+ }
+
+ if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ updateAccessibilityWindowTitleIfNeeded();
+ }
+
+ boolean reCreateWindow = false;
if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
updateDimensions();
computeBounceAnimationScale();
- if (isWindowVisible()) {
- deleteWindowMagnification();
- enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
+ reCreateWindow = true;
+ }
+
+ if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+ reCreateWindow |= handleScreenSizeChanged();
+ }
+
+ // Recreate the window again to correct the window appearance due to density or
+ // window size changed not caused by rotation.
+ if (isWindowVisible() && reCreateWindow) {
+ deleteWindowMagnification();
+ enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
+ }
+ }
+
+ /**
+ * Calculates the magnification frame if the window bounds is changed.
+ * Note that the orientation also changes the wind bounds, so it should be handled first.
+ *
+ * @return {@code true} if the magnification frame is changed with the new window bounds.
+ */
+ private boolean handleScreenSizeChanged() {
+ final Rect oldWindowBounds = new Rect(mWindowBounds);
+ final Rect currentWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
+
+ if (currentWindowBounds.equals(oldWindowBounds)) {
+ if (DEBUG) {
+ Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed");
}
- } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
- onRotate();
- } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
- updateAccessibilityWindowTitleIfNeeded();
+ return false;
}
+ mWindowBounds.set(currentWindowBounds);
+ final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
+ final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
+ setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY);
+ calculateMagnificationFrameBoundary();
+ return true;
}
private void updateSystemUIStateIfNeeded() {
@@ -311,30 +357,42 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mWm.updateViewLayout(mMirrorView, params);
}
- /** Handles MirrorWindow position when the device rotation changed. */
+ /**
+ * Keep MirrorWindow position on the screen unchanged when device rotates 90° clockwise or
+ * anti-clockwise.
+ */
private void onRotate() {
final Display display = mContext.getDisplay();
final int oldRotation = mRotation;
- mWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
-
- setMagnificationFrameBoundary();
mRotation = display.getRotation();
+ final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
+ if (rotationDegree == 0 || rotationDegree == 180) {
+ Log.w(TAG, "onRotate -- rotate with the device. skip it");
+ return;
+ }
+ final Rect currentWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
+ if (currentWindowBounds.width() != mWindowBounds.height()
+ || currentWindowBounds.height() != mWindowBounds.width()) {
+ Log.w(TAG, "onRotate -- unexpected window height/width");
+ return;
+ }
+
+ mWindowBounds.set(currentWindowBounds);
+
+ calculateMagnificationFrameBoundary();
if (!isWindowVisible()) {
return;
}
// Keep MirrorWindow position on the screen unchanged when device rotates 90°
// clockwise or anti-clockwise.
- final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
+
final Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
if (rotationDegree == 90) {
matrix.postTranslate(mWindowBounds.width(), 0);
} else if (rotationDegree == 270) {
matrix.postTranslate(0, mWindowBounds.height());
- } else {
- Log.w(TAG, "Invalid rotation change. " + rotationDegree);
- return;
}
// The rect of MirrorView is going to be transformed.
LayoutParams params =
@@ -440,12 +498,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
- private void setInitialStartBounds() {
+ private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
// Sets the initial frame area for the mirror and places it in the center of the display.
- final int initSize = Math.min(mWindowBounds.width(), mWindowBounds.height()) / 2
+ final int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2
+ 2 * mMirrorSurfaceMargin;
- final int initX = mWindowBounds.width() / 2 - initSize / 2;
- final int initY = mWindowBounds.height() / 2 - initSize / 2;
+ final int initX = centerX - initSize / 2;
+ final int initY = centerY - initSize / 2;
mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
}
@@ -553,7 +611,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mSourceBounds.set(left, top, right, bottom);
}
- private void setMagnificationFrameBoundary() {
+ private void calculateMagnificationFrameBoundary() {
// Calculates width and height for magnification frame could exceed out the screen.
// TODO : re-calculating again when scale is changed.
// The half width of magnification frame.
@@ -644,7 +702,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
: centerY - mMagnificationFrame.exactCenterY();
mScale = Float.isNaN(scale) ? mScale : scale;
- setMagnificationFrameBoundary();
+ calculateMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
if (!isWindowVisible()) {
createMirrorWindow();
@@ -764,6 +822,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
pw.println(" mScale:" + mScale);
pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+ pw.println(" mSourceBounds:"
+ + (isWindowVisible() ? mSourceBounds : "empty"));
pw.println(" mSystemGestureTop:" + mSystemGestureTop);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 169a9c0c6eac..f13730e602a0 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -70,7 +70,7 @@ public final class PhoneStateMonitor {
};
private final Context mContext;
- private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final StatusBarStateController mStatusBarStateController;
private boolean mLauncherShowing;
@@ -78,7 +78,7 @@ public final class PhoneStateMonitor {
@Inject
PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
- Optional<Lazy<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
mContext = context;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
@@ -180,8 +180,7 @@ public final class PhoneStateMonitor {
}
private boolean isBouncerShowing() {
- return mStatusBarOptionalLazy.map(
- statusBarLazy -> statusBarLazy.get().isBouncerShowing()).orElse(false);
+ return mStatusBarOptionalLazy.get().map(StatusBar::isBouncerShowing).orElse(false);
}
private boolean isKeyguardLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index b7344fbc6dda..a2e55c0f76e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -28,6 +28,7 @@ import com.android.systemui.util.ViewController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* Handles:
@@ -42,7 +43,7 @@ import java.io.PrintWriter;
abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
extends ViewController<T> implements Dumpable {
@NonNull final StatusBarStateController mStatusBarStateController;
- @NonNull final StatusBar mStatusBar;
+ @NonNull final Optional<StatusBar> mStatusBarOptional;
@NonNull final DumpManager mDumpManger;
boolean mNotificationShadeExpanded;
@@ -50,11 +51,11 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
protected UdfpsAnimationViewController(
T view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
super(view);
mStatusBarStateController = statusBarStateController;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
mDumpManger = dumpManager;
}
@@ -62,13 +63,17 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
@Override
protected void onViewAttached() {
- mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.addExpansionChangedListener(
+ mStatusBarExpansionChangedListener));
mDumpManger.registerDumpable(getDumpTag(), this);
}
@Override
protected void onViewDetached() {
- mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.removeExpansionChangedListener(
+ mStatusBarExpansionChangedListener));
mDumpManger.unregisterDumpable(getDumpTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 93d80e29aded..85955e1b5d56 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -22,6 +22,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Class that coordinates non-HBM animations for biometric prompt.
*/
@@ -29,9 +31,9 @@ class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
protected UdfpsBpViewController(
@NonNull UdfpsBpView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 6ddf24875000..59d8cd016bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -106,7 +106,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final LayoutInflater mInflater;
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
- @NonNull private final StatusBar mStatusBar;
+ @NonNull private final Optional<StatusBar> mStatusBarOptional;
@NonNull private final StatusBarStateController mStatusBarStateController;
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@@ -496,7 +496,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -521,7 +521,7 @@ public class UdfpsController implements DozeReceiver {
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -739,7 +739,7 @@ public class UdfpsController implements DozeReceiver {
enrollView,
mServerRequest.mEnrollHelper,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mDumpManager
);
case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
@@ -749,7 +749,7 @@ public class UdfpsController implements DozeReceiver {
return new UdfpsKeyguardViewController(
keyguardView,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mKeyguardViewManager,
mKeyguardUpdateMonitor,
mFgExecutor,
@@ -765,7 +765,7 @@ public class UdfpsController implements DozeReceiver {
return new UdfpsBpViewController(
bpView,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mDumpManager
);
case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
@@ -775,7 +775,7 @@ public class UdfpsController implements DozeReceiver {
return new UdfpsFpmOtherViewController(
authOtherView,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mDumpManager
);
default:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 3dab010d917c..54244a16ed78 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -24,6 +24,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Class that coordinates non-HBM animations during enrollment.
*/
@@ -48,9 +50,9 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
@NonNull UdfpsEnrollView view,
@NonNull UdfpsEnrollHelper enrollHelper,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
mEnrollProgressBarRadius = getContext().getResources()
.getInteger(R.integer.config_udfpsEnrollProgressBar);
mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6e2e4baf492b..dcb5aefc8aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -22,6 +22,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
* states.
@@ -32,9 +34,9 @@ class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmO
protected UdfpsFpmOtherViewController(
@NonNull UdfpsFpmOtherView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 51124fb28ad1..bed3fd10208a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -40,6 +40,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
/**
@@ -80,7 +81,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull DelayableExecutor mainDelayableExecutor,
@@ -88,7 +89,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull LockscreenShadeTransitionController transitionController,
@NonNull UdfpsController udfpsController) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mExecutor = mainDelayableExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 567d0cb3e6cb..557bca830f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@ class ControlsUiControllerImpl @Inject constructor (
private val onSeedingComplete = Consumer<Boolean> {
accepted ->
if (accepted) {
- selectedStructure = controlsController.get().getFavorites().maxBy {
+ selectedStructure = controlsController.get().getFavorites().maxByOrNull {
it.controls.size
} ?: EMPTY_STRUCTURE
updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c97a30e6e13e..a94df9efb269 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -60,9 +60,11 @@ import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarA11yHelper;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarOverlayController;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.ReduceBrightColorsController;
@@ -225,7 +227,7 @@ public class DependencyProvider {
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
Optional<Recents> recentsOptional,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -234,6 +236,8 @@ public class DependencyProvider {
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ TaskbarDelegate taskbarDelegate,
UserTracker userTracker) {
return new NavigationBarController(context,
windowManager,
@@ -252,7 +256,7 @@ public class DependencyProvider {
pipOptional,
splitScreenOptional,
recentsOptional,
- statusBarLazy,
+ statusBarOptionalLazy,
shadeController,
notificationRemoteInputManager,
notificationShadeDepthController,
@@ -261,6 +265,8 @@ public class DependencyProvider {
uiEventLogger,
navBarOverlayController,
configurationController,
+ navigationBarA11yHelper,
+ taskbarDelegate,
userTracker);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index bc4ced452630..04a0226aa0b4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -81,6 +81,7 @@ import com.android.systemui.util.leak.RotationUtils;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -172,7 +173,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
SysUiState sysUiState,
@Main Handler handler,
PackageManager packageManager,
- StatusBar statusBar) {
+ Optional<StatusBar> statusBarOptional) {
super(context,
windowManagerFuncs,
@@ -199,12 +200,11 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
iWindowManager,
backgroundExecutor,
uiEventLogger,
- null,
ringerModeTracker,
sysUiState,
handler,
packageManager,
- statusBar);
+ statusBarOptional);
mLockPatternUtils = lockPatternUtils;
mKeyguardStateController = keyguardStateController;
@@ -334,12 +334,11 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- StatusBar statusBar) {
+ Optional<StatusBar> statusBarOptional) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, sysuiColorExtractor, statusBarService,
notificationShadeWindowController, sysuiState, onRotateCallback,
- keyguardShowing, powerAdapter, uiEventLogger, null,
- statusBar);
+ keyguardShowing, powerAdapter, uiEventLogger, statusBarOptional);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 06e74821869e..b4e0e2f947a6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -131,6 +131,7 @@ import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -190,7 +191,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
private final SysUiState mSysUiState;
- private final GlobalActionsInfoProvider mInfoProvider;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -230,7 +230,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
- private final StatusBar mStatusBar;
+ private final Optional<StatusBar> mStatusBarOptional;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -333,12 +333,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
- GlobalActionsInfoProvider infoProvider,
RingerModeTracker ringerModeTracker,
SysUiState sysUiState,
@Main Handler handler,
PackageManager packageManager,
- StatusBar statusBar) {
+ Optional<StatusBar> statusBarOptional) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -358,7 +357,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mTelecomManager = telecomManager;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -368,7 +366,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSysUiState = sysUiState;
mMainHandler = handler;
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -418,8 +416,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mUiEventLogger;
}
- protected StatusBar getStatusBar() {
- return mStatusBar;
+ protected Optional<StatusBar> getStatusBar() {
+ return mStatusBarOptional;
}
/**
@@ -653,7 +651,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider, mStatusBar);
+ mStatusBarOptional);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -850,7 +848,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
if (mTelecomManager != null) {
// Close shade so user sees the activity
- mStatusBar.collapseShade();
+ mStatusBarOptional.ifPresent(StatusBar::collapseShade);
Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
null /* number */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -982,7 +980,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mIActivityManager.requestInteractiveBugReport();
}
// Close shade so user sees the activity
- mStatusBar.collapseShade();
+ mStatusBarOptional.ifPresent(StatusBar::collapseShade);
} catch (RemoteException e) {
}
}
@@ -1002,7 +1000,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
mIActivityManager.requestFullBugReport();
// Close shade so user sees the activity
- mStatusBar.collapseShade();
+ mStatusBarOptional.ifPresent(StatusBar::collapseShade);
} catch (RemoteException e) {
}
return false;
@@ -2119,9 +2117,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
- private GlobalActionsInfoProvider mInfoProvider;
private GestureDetector mGestureDetector;
- private StatusBar mStatusBar;
+ private Optional<StatusBar> mStatusBarOptional;
protected ViewGroup mContainer;
@@ -2146,7 +2143,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (distanceY < 0 && distanceY > distanceX
- && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ && e1.getY() <= mStatusBarOptional.map(
+ StatusBar::getStatusBarHeight).orElse(0)) {
// Downwards scroll from top
openShadeAndDismiss();
return true;
@@ -2158,7 +2156,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
- && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ && e1.getY() <= mStatusBarOptional.map(
+ StatusBar::getStatusBarHeight).orElse(0)) {
// Downwards fling from top
openShadeAndDismiss();
return true;
@@ -2173,7 +2172,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) {
+ Optional<StatusBar> statusBarOptional) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2186,8 +2185,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
@@ -2218,12 +2216,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private void openShadeAndDismiss() {
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- if (mStatusBar.isKeyguardShowing()) {
+ if (mStatusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
// match existing lockscreen behavior to open QS when swiping from status bar
- mStatusBar.animateExpandSettingsPanel(null);
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.animateExpandSettingsPanel(null));
} else {
// otherwise, swiping down should expand notification shade
- mStatusBar.animateExpandNotificationsPanel();
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.animateExpandNotificationsPanel());
}
dismiss();
}
@@ -2304,10 +2304,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mBackgroundDrawable = new ScrimDrawable();
mScrimAlpha = 1.0f;
}
-
- if (mInfoProvider != null && mInfoProvider.shouldShowMessage()) {
- mInfoProvider.addPanel(mContext, mContainer, mAdapter.getCount(), () -> dismiss());
- }
}
protected void fixNavBarClipping() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
deleted file mode 100644
index 25837e3aacdf..000000000000
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ /dev/null
@@ -1,120 +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.globalactions
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.content.res.Configuration
-import android.net.Uri
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.plugins.ActivityStarter
-import javax.inject.Inject
-
-private const val TAG = "GlobalActionsInfo"
-
-/** Maximum number of times to show change info message */
-private const val MAX_VIEW_COUNT = 3
-
-/** Maximum number of buttons allowed in landscape before this panel does not fit */
-private const val MAX_BUTTONS_LANDSCAPE = 4
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-class GlobalActionsInfoProvider @Inject constructor(
- private val context: Context,
- private val walletClient: QuickAccessWalletClient,
- private val controlsController: ControlsController,
- private val activityStarter: ActivityStarter
-) {
-
- private var pendingIntent: PendingIntent
-
- init {
- val url = context.resources.getString(R.string.global_actions_change_url)
- val intent = Intent(Intent.ACTION_VIEW).apply {
- setData(Uri.parse(url))
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
- pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
- }
-
- fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) {
- // This panel does not fit on landscape with two rows of buttons showing,
- // so skip adding the panel (and incrementing view counT) in that case
- val isLandscape =
- context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
- if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) {
- return
- }
-
- val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
- parent, false)
-
- val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title)
- val message = view.findViewById<TextView>(R.id.global_actions_change_message)
- message?.setText(context.getString(R.string.global_actions_change_description, walletTitle))
-
- view.setOnClickListener { _ ->
- dismissParent.run()
- activityStarter.postStartActivityDismissingKeyguard(pendingIntent)
- }
- parent.addView(view, 0) // Add to top
- incrementViewCount()
- }
-
- fun shouldShowMessage(): Boolean {
- // This is only relevant for some devices
- val isEligible = context.resources.getBoolean(
- R.bool.global_actions_show_change_info)
- if (!isEligible) {
- return false
- }
-
- val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
-
- // Only show to users who previously had these items set up
- val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) {
- sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
- } else {
- -1
- }
-
- // Limit number of times this is displayed
- return viewCount > -1 && viewCount < MAX_VIEW_COUNT
- }
-
- private fun hadContent(): Boolean {
- // Check whether user would have seen content in the power menu that has now moved
- val hadControls = controlsController.getFavorites().size > 0
- val hadCards = walletClient.isWalletFeatureAvailable
- Log.d(TAG, "Previously had controls $hadControls, cards $hadCards")
- return hadControls || hadCards
- }
-
- private fun incrementViewCount() {
- val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
- val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
- sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 26f38ddd5919..e762839c96ed 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -23,10 +23,12 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
@@ -40,10 +42,12 @@ import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSE
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -54,9 +58,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IdRes;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -68,6 +70,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -89,6 +92,7 @@ import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -101,7 +105,6 @@ import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.VisibleForTesting;
@@ -130,6 +133,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -151,7 +155,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
-import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Consumer;
@@ -162,8 +165,7 @@ import dagger.Lazy;
* Contains logic for a navigation bar view.
*/
public class NavigationBar implements View.OnAttachStateChangeListener,
- Callbacks, NavigationModeController.ModeChangedListener,
- AccessibilityButtonModeObserver.ModeChangedListener {
+ Callbacks, NavigationModeController.ModeChangedListener {
public static final String TAG = "NavigationBar";
private static final boolean DEBUG = false;
@@ -180,13 +182,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Context mContext;
private final WindowManager mWindowManager;
private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
private final SysUiState mSysUiFlagsContainer;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final OverviewProxyService mOverviewProxyService;
@@ -201,11 +202,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private final UserTracker mUserTracker;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
private NavigationBarView mNavigationBarView;
+ private NavigationBarFrame mFrame;
private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -381,6 +384,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
@Override
+ public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+ mNavigationBarView
+ .getFloatingRotationButton()
+ .onTaskbarStateChanged(visible, stashed);
+ }
+
+ @Override
public void onToggleRecentApps() {
// The same case as onOverviewShown but only for 3-button navigation.
mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
@@ -475,7 +485,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
- Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
+ Optional<Recents> recentsOptional,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -483,17 +494,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
+ NavigationBarA11yHelper navigationBarA11yHelper,
UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
- mAccessibilityManagerWrapper = accessibilityManagerWrapper;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
mSysUiFlagsContainer = sysUiFlagsContainer;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mOverviewProxyService = overviewProxyService;
@@ -508,11 +519,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mUserTracker = userTracker;
mNotificationShadeDepthController = notificationShadeDepthController;
mNavBarMode = mNavigationModeController.addListener(this);
- mAccessibilityButtonModeObserver.addListener(this);
}
public NavigationBarView getView() {
@@ -520,34 +531,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
public View createView(Bundle savedState) {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_SLIPPERY,
- PixelFormat.TRANSLUCENT);
- lp.token = new Binder();
- lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
- lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- lp.windowAnimations = 0;
- lp.setTitle("NavigationBar" + mContext.getDisplayId());
- lp.setFitInsetsTypes(0 /* types */);
- lp.setTrustedOverlay();
-
- NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
+ mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
R.layout.navigation_bar_window, null);
- View barView = LayoutInflater.from(frame.getContext()).inflate(
- R.layout.navigation_bar, frame);
+ View barView = LayoutInflater.from(mFrame.getContext()).inflate(
+ R.layout.navigation_bar, mFrame);
barView.addOnAttachStateChangeListener(this);
mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
- mContext.getSystemService(WindowManager.class).addView(frame, lp);
+ mContext.getSystemService(WindowManager.class).addView(mFrame,
+ getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
+ .getRotation()));
mDisplayId = mContext.getDisplayId();
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -605,9 +599,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mContext.getSystemService(WindowManager.class).removeViewImmediate(
mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mAccessibilityButtonModeObserver.removeListener(this);
- mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mAssistContentObserver);
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -618,7 +611,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onViewAttachedToWindow(View v) {
final Display display = v.getDisplay();
- mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
+ mNavigationBarView.setComponents(mStatusBarOptionalLazy.get().get().getPanelController());
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
@@ -629,7 +622,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -704,6 +697,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
+ mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
}
@@ -722,6 +716,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
* Called when a non-reloading configuration change happens and we need to update.
*/
public void onConfigurationChanged(Configuration newConfig) {
+ final int rotation = newConfig.windowConfiguration.getRotation();
final Locale locale = mContext.getResources().getConfiguration().locale;
final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
if (!locale.equals(mLocale) || ld != mLayoutDirection) {
@@ -735,9 +730,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
refreshLayout(ld);
}
- repositionNavigationBar();
+ repositionNavigationBar(rotation);
if (canShowSecondaryHandle()) {
- int rotation = newConfig.windowConfiguration.getRotation();
if (rotation != mCurrentRotation) {
mCurrentRotation = rotation;
orientSecondaryHomeHandle();
@@ -889,30 +883,15 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return;
}
boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
- int hints = mNavigationIconHints;
- switch (backDisposition) {
- case InputMethodService.BACK_DISPOSITION_DEFAULT:
- case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
- case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
- if (imeShown) {
- hints |= NAVIGATION_HINT_BACK_ALT;
- } else {
- hints &= ~NAVIGATION_HINT_BACK_ALT;
- }
- break;
- case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
- hints &= ~NAVIGATION_HINT_BACK_ALT;
- break;
- }
- if (showImeSwitcher) {
- hints |= NAVIGATION_HINT_IME_SHOWN;
- } else {
- hints &= ~NAVIGATION_HINT_IME_SHOWN;
- }
+ int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ imeShown, showImeSwitcher);
if (hints == mNavigationIconHints) return;
mNavigationIconHints = hints;
- mNavigationBarView.setNavigationIconHints(hints);
+ if (!isTablet(mContext)) {
+ // All IME functions handled by launcher via Sysui flags for large screen
+ mNavigationBarView.setNavigationIconHints(hints);
+ }
checkBarModes();
updateSystemUiStateFlags(-1);
}
@@ -984,7 +963,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
if (displayId != mDisplayId) {
return;
}
@@ -1120,13 +1099,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|| (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
}
- private void repositionNavigationBar() {
- if (!mNavigationBarView.isAttachedToWindow()) return;
+ private void repositionNavigationBar(int rotation) {
+ if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
prepareNavigationBarView();
- mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
- ((View) mNavigationBarView.getParent()).getLayoutParams());
+ mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation));
}
private void updateScreenPinningGestures() {
@@ -1168,7 +1146,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState(mAccessibilityManager);
+ updateAccessibilityServicesState();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1184,13 +1162,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallUI at this point,
// and their ONLY options are to answer or reject the call.)
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHomeBlockedThisTouch = false;
TelecomManager telecomManager =
mContext.getSystemService(TelecomManager.class);
if (telecomManager != null && telecomManager.isRinging()) {
- if (mStatusBarLazy.get().isKeyguardShowing()) {
+ if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
"No heads up");
mHomeBlockedThisTouch = true;
@@ -1206,14 +1185,15 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
- mStatusBarLazy.get().awakenDreams();
+ statusBarOptional.ifPresent(StatusBar::awakenDreams);
break;
}
return false;
}
private void onVerticalChanged(boolean isVertical) {
- mStatusBarLazy.get().setQsScrimEnabled(!isVertical);
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.setQsScrimEnabled(!isVertical));
}
private boolean onNavigationTouch(View v, MotionEvent event) {
@@ -1239,7 +1219,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
AssistManager.INVOCATION_TYPE_KEY,
AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
mAssistManagerLazy.get().startAssist(args);
- mStatusBarLazy.get().awakenDreams();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
mNavigationBarView.abortCurrentGesture();
return true;
}
@@ -1265,7 +1245,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
LatencyTracker.getInstance(mContext).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
- mStatusBarLazy.get().awakenDreams();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
mCommandQueue.toggleRecentApps();
}
@@ -1370,8 +1350,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return false;
}
- return mStatusBarLazy.get().toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
- MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
+ return mStatusBarOptionalLazy.get().map(
+ statusBar -> statusBar.toggleSplitScreenMode(
+ MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
+ MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS))
+ .orElse(false);
}
private void onAccessibilityClick(View v) {
@@ -1389,9 +1372,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return true;
}
- void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
- boolean[] feedbackEnabled = new boolean[1];
- int a11yFlags = getA11yButtonState(feedbackEnabled);
+ void updateAccessibilityServicesState() {
+ int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1410,7 +1392,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = getA11yButtonState(null);
+ a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1420,6 +1402,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
@@ -1435,45 +1419,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
- /**
- * Returns the system UI flags corresponding the the current accessibility button state
- *
- * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
- */
- public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
- boolean feedbackEnabled = false;
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<AccessibilityServiceInfo> services =
- mAccessibilityManager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
- for (int i = services.size() - 1; i >= 0; --i) {
- AccessibilityServiceInfo info = services.get(i);
- if (info.feedbackType != 0 && info.feedbackType !=
- AccessibilityServiceInfo.FEEDBACK_GENERIC) {
- feedbackEnabled = true;
- }
- }
-
- if (outFeedbackEnabled != null) {
- outFeedbackEnabled[0] = feedbackEnabled;
- }
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
private void updateAssistantEntrypoints() {
mAssistantAvailable = mAssistManagerLazy.get()
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
@@ -1535,7 +1480,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private void checkBarModes() {
// We only have status bar on default display now.
if (mIsOnDefaultDisplay) {
- mStatusBarLazy.get().checkBarModes();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::checkBarModes);
} else {
checkNavBarModes();
}
@@ -1553,7 +1498,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
* Checks current navigation bar mode and make transitions.
*/
public void checkNavBarModes() {
- final boolean anim = mStatusBarLazy.get().isDeviceInteractive()
+ final boolean anim =
+ mStatusBarOptionalLazy.get().map(StatusBar::isDeviceInteractive).orElse(false)
&& mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
}
@@ -1575,11 +1521,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- updateAccessibilityServicesState(mAccessibilityManager);
- }
-
public void disableAnimationsDuringHide(long delay) {
mNavigationBarView.setLayoutTransitionsEnabled(false);
mHandler.postDelayed(mEnableLayoutTransitions,
@@ -1604,22 +1545,110 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final AccessibilityServicesStateChangeListener mAccessibilityListener =
+ private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
this::updateAccessibilityServicesState;
+ private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
+ WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
+ lp.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
+ }
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
+ int width = WindowManager.LayoutParams.MATCH_PARENT;
+ int height = WindowManager.LayoutParams.MATCH_PARENT;
+ int insetsHeight = -1;
+ int gravity = Gravity.BOTTOM;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ boolean navBarCanMove = true;
+ if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) {
+ Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
+ navBarCanMove = displaySize.width() != displaySize.height()
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_navBarCanMove);
+ }
+ if (!navBarCanMove) {
+ height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ insetsHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ } else {
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ insetsHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ break;
+ case Surface.ROTATION_90:
+ gravity = Gravity.RIGHT;
+ width = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width);
+ break;
+ case Surface.ROTATION_270:
+ gravity = Gravity.LEFT;
+ width = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width);
+ break;
+ }
+ }
+ }
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ width,
+ height,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_SLIPPERY,
+ PixelFormat.TRANSLUCENT);
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ lp.gravity = gravity;
+ if (insetsHeight != -1) {
+ lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0);
+ } else {
+ lp.providedInternalInsets = Insets.NONE;
+ }
+ }
+ lp.token = new Binder();
+ lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ lp.windowAnimations = 0;
+ lp.setTitle("NavigationBar" + mContext.getDisplayId());
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.setTrustedOverlay();
+ return lp;
+ }
+
private boolean canShowSecondaryHandle() {
return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
}
private final Consumer<Integer> mRotationWatcher = rotation -> {
- if (mNavigationBarView.needsReorient(rotation)) {
- repositionNavigationBar();
+ if (mNavigationBarView != null
+ && mNavigationBarView.needsReorient(rotation)) {
+ repositionNavigationBar(rotation);
}
};
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // This receiver is unregistered when the view is detached, but on devices with multiple
+ // displays, it can sometimes still receive an ACTION_SCREEN_ON/ACTION_SCREEN_OFF on
+ // display switch, after it was detached, so this null check ensures no crash in that
+ // scenario.
+ if (mNavigationBarView == null) {
+ return;
+ }
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
@@ -1628,7 +1657,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState(mAccessibilityManager);
+ updateAccessibilityServicesState();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
new file mode 100644
index 000000000000..13e6d8b410d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
@@ -0,0 +1,90 @@
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Extracts shared elements of a11y necessary between navbar and taskbar delegate
+ */
+@SysUISingleton
+public final class NavigationBarA11yHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener {
+ private final AccessibilityManager mAccessibilityManager;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
+
+ @Inject
+ public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
+ mAccessibilityManager = accessibilityManager;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ }
+
+ public void registerA11yEventListener(NavA11yEventListener listener) {
+ mA11yEventListeners.add(listener);
+ }
+
+ public void removeA11yEventListener(NavA11yEventListener listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchEventUpdate() {
+ for (NavA11yEventListener listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ public interface NavA11yEventListener {
+ void updateAccessibilityServicesState();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index b9e9240b354a..458c50d9cb81 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,16 +19,15 @@ package com.android.systemui.navigationbar;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -86,8 +85,6 @@ public class NavigationBarController implements Callbacks,
ConfigurationController.ConfigurationListener,
NavigationModeController.ModeChangedListener, Dumpable {
- private static final float TABLET_MIN_DPS = 600;
-
private static final String TAG = NavigationBarController.class.getSimpleName();
private final Context mContext;
@@ -107,12 +104,13 @@ public class NavigationBarController implements Callbacks,
private final Optional<Pip> mPipOptional;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final Optional<Recents> mRecentsOptional;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final SystemActions mSystemActions;
private final UiEventLogger mUiEventLogger;
private final Handler mHandler;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private final DisplayManager mDisplayManager;
private final NavigationBarOverlayController mNavBarOverlayController;
private final TaskbarDelegate mTaskbarDelegate;
@@ -148,7 +146,7 @@ public class NavigationBarController implements Callbacks,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
Optional<Recents> recentsOptional,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -157,6 +155,8 @@ public class NavigationBarController implements Callbacks,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ TaskbarDelegate taskbarDelegate,
UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
@@ -175,13 +175,14 @@ public class NavigationBarController implements Callbacks,
mPipOptional = pipOptional;
mSplitScreenOptional = splitScreenOptional;
mRecentsOptional = recentsOptional;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationShadeDepthController = notificationShadeDepthController;
mSystemActions = systemActions;
mUiEventLogger = uiEventLogger;
mHandler = mainHandler;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -189,15 +190,17 @@ public class NavigationBarController implements Callbacks,
mNavBarOverlayController = navBarOverlayController;
mNavMode = mNavigationModeController.addListener(this);
mNavigationModeController.addListener(this);
- mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
- mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mTaskbarDelegate = taskbarDelegate;
+ mTaskbarDelegate.setOverviewProxyService(overviewProxyService,
+ navigationBarA11yHelper, mSysUiFlagsContainer);
+ mIsTablet = isTablet(mContext);
mUserTracker = userTracker;
}
@Override
public void onConfigChanged(Configuration newConfig) {
boolean isOldConfigTablet = mIsTablet;
- mIsTablet = isTablet(newConfig);
+ mIsTablet = isTablet(newConfig, mContext);
boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
// If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
if (largeScreenChanged && updateNavbarForTaskbar()) {
@@ -237,25 +240,28 @@ public class NavigationBarController implements Callbacks,
});
}
- /**
- * @return {@code true} if navbar was added/removed, false otherwise
- */
- public boolean updateNavbarForTaskbar() {
- if (!isThreeButtonTaskbarFlagEnabled()) {
- return false;
+ /** @see #initializeTaskbarIfNecessary() */
+ private boolean updateNavbarForTaskbar() {
+ boolean taskbarShown = initializeTaskbarIfNecessary();
+ if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
+ createNavigationBar(mContext.getDisplay(), null, null);
}
+ return taskbarShown;
+ }
- if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
+ /** @return {@code true} if taskbar is enabled, false otherwise */
+ private boolean initializeTaskbarIfNecessary() {
+ boolean isShowingTaskbar = mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON;
+ if (isShowingTaskbar) {
// Remove navigation bar when taskbar is showing, currently only for 3 button mode
removeNavigationBar(mContext.getDisplayId());
mCommandQueue.addCallback(mTaskbarDelegate);
- } else if (mNavigationBars.get(mContext.getDisplayId()) == null) {
- // Add navigation bar after taskbar goes away
- createNavigationBar(mContext.getDisplay(), null, null);
+ mTaskbarDelegate.init(mContext.getDisplayId());
+ } else {
mCommandQueue.removeCallback(mTaskbarDelegate);
+ mTaskbarDelegate.destroy();
}
-
- return true;
+ return isShowingTaskbar;
}
@Override
@@ -266,7 +272,7 @@ public class NavigationBarController implements Callbacks,
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
- mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mIsTablet = isTablet(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -302,7 +308,7 @@ public class NavigationBarController implements Callbacks,
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
- if (updateNavbarForTaskbar()) {
+ if (initializeTaskbarIfNecessary()) {
return;
}
@@ -326,7 +332,7 @@ public class NavigationBarController implements Callbacks,
return;
}
- if (isThreeButtonTaskbarEnabled()) {
+ if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
return;
}
@@ -363,7 +369,7 @@ public class NavigationBarController implements Callbacks,
mPipOptional,
mSplitScreenOptional,
mRecentsOptional,
- mStatusBarLazy,
+ mStatusBarOptionalLazy,
mShadeController,
mNotificationRemoteInputManager,
mNotificationShadeDepthController,
@@ -371,6 +377,7 @@ public class NavigationBarController implements Callbacks,
mHandler,
mNavBarOverlayController,
mUiEventLogger,
+ mNavigationBarA11yHelper,
mUserTracker);
mNavigationBars.put(displayId, navBar);
@@ -462,24 +469,6 @@ public class NavigationBarController implements Callbacks,
return mNavigationBars.get(DEFAULT_DISPLAY);
}
- private boolean isThreeButtonTaskbarEnabled() {
- return mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON &&
- isThreeButtonTaskbarFlagEnabled();
- }
-
- private boolean isThreeButtonTaskbarFlagEnabled() {
- return SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
- }
-
- private boolean isTablet(Configuration newConfig) {
- float density = Resources.getSystem().getDisplayMetrics().density;
- int size = Math.min((int) (density * newConfig.screenWidthDp),
- (int) (density* newConfig.screenHeightDp));
- DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
- float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- return (size / densityRatio) >= TABLET_MIN_DPS;
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
for (int i = 0; i < mNavigationBars.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 4816f1cf8d6a..23c066a75732 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -69,6 +69,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.ContextualButton;
import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
@@ -275,14 +276,23 @@ public class NavigationBarView extends FrameLayout implements
false /* inScreen */, false /* useNearestRegion */));
};
- private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
- if (visible) {
- // If the button will actually become visible and the navbar is about to hide,
- // tell the statusbar to keep it around for longer
- mAutoHideController.touchAutoHide();
- }
- notifyActiveTouchRegions();
- };
+ private final RotationButtonUpdatesCallback mRotationButtonListener =
+ new RotationButtonUpdatesCallback() {
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ if (visible) {
+ // If the button will actually become visible and the navbar is about
+ // to hide, tell the statusbar to keep it around for longer
+ mAutoHideController.touchAutoHide();
+ }
+ notifyActiveTouchRegions();
+ }
+
+ @Override
+ public void onPositionChanged() {
+ notifyActiveTouchRegions();
+ }
+ };
private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
if (visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
index e48785844347..3486c6e75931 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
@@ -20,12 +20,10 @@ import android.view.View;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-import java.util.function.Consumer;
-
/** Interface of a rotation button that interacts {@link RotationButtonController}. */
public interface RotationButton {
void setRotationButtonController(RotationButtonController rotationButtonController);
- void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback);
+ void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback);
View getCurrentView();
boolean show();
boolean hide();
@@ -39,4 +37,12 @@ public interface RotationButton {
default boolean acceptRotationProposal() {
return getCurrentView() != null;
}
+
+ /**
+ * Callback for updates provided by a rotation button
+ */
+ interface RotationButtonUpdatesCallback {
+ void onVisibilityChanged(boolean isVisible);
+ void onPositionChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 649ac875b4c2..196625b3630e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -46,7 +46,10 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
+import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.utilities.ViewRippler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -138,12 +141,12 @@ public class RotationButtonController {
}
void setRotationButton(RotationButton rotationButton,
- Consumer<Boolean> visibilityChangedCallback) {
+ RotationButtonUpdatesCallback updatesCallback) {
mRotationButton = rotationButton;
mRotationButton.setRotationButtonController(this);
mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
- mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback);
+ mRotationButton.setUpdatesCallback(updatesCallback);
}
void registerListeners() {
@@ -311,7 +314,7 @@ public class RotationButtonController {
// Prepare to show the navbar icon by updating the icon style to change anim params
mLastRotationSuggestion = rotation; // Remember rotation for click
- final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
+ final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation);
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
mIconResId = rotationCCW
? R.drawable.ic_sysbar_rotate_button_ccw_start_90
@@ -431,23 +434,6 @@ public class RotationButtonController {
return rotation == NATURAL_ROTATION;
}
- private boolean isRotationAnimationCCW(int from, int to) {
- // All 180deg WM rotation animations are CCW, match that
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
- return false; // Default
- }
-
private void rescheduleRotationTimeout(final boolean reasonHover) {
// May be called due to a new rotation proposal or a change in hover state
if (reasonHover) {
@@ -520,38 +506,6 @@ public class RotationButtonController {
}
}
- private class ViewRippler {
- private static final int RIPPLE_OFFSET_MS = 50;
- private static final int RIPPLE_INTERVAL_MS = 2000;
- private View mRoot;
-
- public void start(View root) {
- stop(); // Stop any pending ripple animations
-
- mRoot = root;
-
- // Schedule pending ripples, offset the 1st to avoid problems with visibility change
- mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
- mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
- }
-
- public void stop() {
- if (mRoot != null) mRoot.removeCallbacks(mRipple);
- }
-
- private final Runnable mRipple = new Runnable() {
- @Override
- public void run() { // Cause the ripple to fire via false presses
- if (!mRoot.isAttachedToWindow()) return;
- mRoot.setPressed(true /* pressed */);
- mRoot.setPressed(false /* pressed */);
- }
- };
- }
-
enum RotationButtonEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The rotation button was shown")
ROTATION_SUGGESTION_SHOWN(206),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 03147d8f1085..1d4414690221 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -16,23 +16,100 @@
package com.android.systemui.navigationbar;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+
+import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.view.InsetsState;
+import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.statusbar.CommandQueue;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class TaskbarDelegate implements CommandQueue.Callbacks {
- private final OverviewProxyService mOverviewProxyService;
+ private OverviewProxyService mOverviewProxyService;
+ private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private SysUiState mSysUiState;
+ private int mDisplayId;
+ private int mNavigationIconHints;
+ private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
+ this::updateSysuiFlags;
+ @Inject
+ public TaskbarDelegate() { /* no-op */ }
- public TaskbarDelegate(OverviewProxyService overviewProxyService) {
+ public void setOverviewProxyService(OverviewProxyService overviewProxyService,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ SysUiState sysUiState) {
+ // TODO: adding this in the ctor results in a dagger dependency cycle :(
mOverviewProxyService = overviewProxyService;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mSysUiState = sysUiState;
+ }
+
+ public void destroy() {
+ mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ }
+
+ public void init(int displayId) {
+ mDisplayId = displayId;
+ mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ // Set initial state for any listeners
+ updateSysuiFlags();
+ }
+
+ private void updateSysuiFlags() {
+ int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+ boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+
+ mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
+ .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
+ .setFlag(SYSUI_STATE_IME_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ .commitUpdate(mDisplayId);
}
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
- mOverviewProxyService.notifyImeWindowStatus(displayId, token, vis, backDisposition,
- showImeSwitcher);
+ boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ imeShown, showImeSwitcher);
+ if (hints != mNavigationIconHints) {
+ mNavigationIconHints = hints;
+ updateSysuiFlags();
+ }
+ }
+
+ @Override
+ public void onRotationProposal(int rotation, boolean isValid) {
+ mOverviewProxyService.onRotationProposal(rotation, isValid);
+ }
+
+ @Override
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ mOverviewProxyService.disable(displayId, state1, state2, animate);
+ }
+
+ @Override
+ public void onSystemBarAttributesChanged(int displayId, int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
+ InsetsState requestedState, String packageName) {
+ mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
index 6a97a3379939..ebb67af43a37 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
@@ -23,10 +23,6 @@ import android.view.View;
import com.android.systemui.navigationbar.RotationButton;
import com.android.systemui.navigationbar.RotationButtonController;
-import com.android.systemui.navigationbar.buttons.ContextualButton;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-
-import java.util.function.Consumer;
/** Containing logic for the rotation button in nav bar. */
public class RotationContextButton extends ContextualButton implements RotationButton {
@@ -48,13 +44,10 @@ public class RotationContextButton extends ContextualButton implements RotationB
}
@Override
- public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
- setListener(new ContextButtonListener() {
- @Override
- public void onVisibilityChanged(ContextualButton button, boolean visible) {
- if (visibilityChangedCallback != null) {
- visibilityChangedCallback.accept(visible);
- }
+ public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
+ setListener((button, visible) -> {
+ if (updatesCallback != null) {
+ updatesCallback.onVisibilityChanged(visible);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
index 61118c5d26ac..46057952e079 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
@@ -20,48 +20,72 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.Surface;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.navigationbar.RotationButton;
import com.android.systemui.navigationbar.RotationButtonController;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
+import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position;
-import java.util.function.Consumer;
-
-/** Containing logic for the rotation button on the physical left bottom corner of the screen. */
+/**
+ * Containing logic for the rotation button on the physical left bottom corner of the screen.
+ */
public class FloatingRotationButton implements RotationButton {
private static final float BACKGROUND_ALPHA = 0.92f;
+ private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
- private final Context mContext;
private final WindowManager mWindowManager;
+ private final ViewGroup mKeyButtonContainer;
private final KeyButtonView mKeyButtonView;
- private final int mDiameter;
- private final int mMargin;
+
+ private final int mContainerSize;
+
private KeyButtonDrawable mKeyButtonDrawable;
private boolean mIsShowing;
private boolean mCanShow = true;
+ private int mDisplayRotation;
+
+ private boolean mIsTaskbarVisible = false;
+ private boolean mIsTaskbarStashed = false;
+
+ private final FloatingRotationButtonPositionCalculator mPositionCalculator;
private RotationButtonController mRotationButtonController;
- private Consumer<Boolean> mVisibilityChangedCallback;
+ private RotationButtonUpdatesCallback mUpdatesCallback;
+ private Position mPosition;
public FloatingRotationButton(Context context) {
- mContext = context;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate(
+ mWindowManager = context.getSystemService(WindowManager.class);
+ mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(
R.layout.rotate_suggestion, null);
+ mKeyButtonView = mKeyButtonContainer.findViewById(R.id.rotate_suggestion);
mKeyButtonView.setVisibility(View.VISIBLE);
- Resources res = mContext.getResources();
- mDiameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
- mMargin = Math.max(res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
+ Resources res = context.getResources();
+
+ int defaultMargin = Math.max(
+ res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
res.getDimensionPixelSize(R.dimen.rounded_corner_content_padding));
+
+ int taskbarMarginLeft =
+ res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_left_margin);
+ int taskbarMarginBottom =
+ res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_bottom_margin);
+
+ mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
+ taskbarMarginLeft, taskbarMarginBottom);
+
+ final int diameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
+ mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
+ taskbarMarginBottom));
}
@Override
@@ -72,8 +96,8 @@ public class FloatingRotationButton implements RotationButton {
}
@Override
- public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
- mVisibilityChangedCallback = visibilityChangedCallback;
+ public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
+ mUpdatesCallback = updatesCallback;
}
@Override
@@ -86,45 +110,39 @@ public class FloatingRotationButton implements RotationButton {
if (!mCanShow || mIsShowing) {
return false;
}
+
mIsShowing = true;
int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
- mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mContainerSize,
+ mContainerSize,
+ 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
PixelFormat.TRANSLUCENT);
+
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
lp.setFitInsetsTypes(0 /*types */);
- switch (mWindowManager.getDefaultDisplay().getRotation()) {
- case Surface.ROTATION_0:
- lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
- break;
- case Surface.ROTATION_90:
- lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
- break;
- case Surface.ROTATION_180:
- lp.gravity = Gravity.TOP | Gravity.RIGHT;
- break;
- case Surface.ROTATION_270:
- lp.gravity = Gravity.TOP | Gravity.LEFT;
- break;
- default:
- break;
- }
- mWindowManager.addView(mKeyButtonView, lp);
+
+ mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+ mPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ lp.gravity = mPosition.getGravity();
+ ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+ mPosition.getGravity();
+
+ updateTranslation(mPosition, /* animate */ false);
+
+ mWindowManager.addView(mKeyButtonContainer, lp);
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
mKeyButtonDrawable.resetAnimation();
mKeyButtonDrawable.startAnimation();
}
- mKeyButtonView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5,
- int i6, int i7) {
- if (mIsShowing && mVisibilityChangedCallback != null) {
- mVisibilityChangedCallback.accept(true);
- }
- mKeyButtonView.removeOnLayoutChangeListener(this);
- }
- });
+
+ if (mUpdatesCallback != null) {
+ mUpdatesCallback.onVisibilityChanged(true);
+ }
+
return true;
}
@@ -133,10 +151,10 @@ public class FloatingRotationButton implements RotationButton {
if (!mIsShowing) {
return false;
}
- mWindowManager.removeViewImmediate(mKeyButtonView);
+ mWindowManager.removeViewImmediate(mKeyButtonContainer);
mIsShowing = false;
- if (mVisibilityChangedCallback != null) {
- mVisibilityChangedCallback.accept(false);
+ if (mUpdatesCallback != null) {
+ mUpdatesCallback.onVisibilityChanged(false);
}
return true;
}
@@ -183,4 +201,43 @@ public class FloatingRotationButton implements RotationButton {
hide();
}
}
+
+ public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {
+ mIsTaskbarVisible = taskbarVisible;
+ mIsTaskbarStashed = taskbarStashed;
+
+ if (!mIsShowing) return;
+
+ final Position newPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ if (newPosition.getTranslationX() != mPosition.getTranslationX()
+ || newPosition.getTranslationY() != mPosition.getTranslationY()) {
+ updateTranslation(newPosition, /* animate */ true);
+ mPosition = newPosition;
+ }
+ }
+
+ private void updateTranslation(Position position, boolean animate) {
+ final int translationX = position.getTranslationX();
+ final int translationY = position.getTranslationY();
+
+ if (animate) {
+ mKeyButtonView
+ .animate()
+ .translationX(translationX)
+ .translationY(translationY)
+ .setDuration(MARGIN_ANIMATION_DURATION_MILLIS)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .withEndAction(() -> {
+ if (mUpdatesCallback != null && mIsShowing) {
+ mUpdatesCallback.onPositionChanged();
+ }
+ })
+ .start();
+ } else {
+ mKeyButtonView.setTranslationX(translationX);
+ mKeyButtonView.setTranslationY(translationY);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt
new file mode 100644
index 000000000000..3ce51ad331c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt
@@ -0,0 +1,65 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.view.Gravity
+import android.view.Surface
+
+/**
+ * Calculates gravity and translation that is necessary to display
+ * the button in the correct position based on the current state
+ */
+internal class FloatingRotationButtonPositionCalculator(
+ private val defaultMargin: Int,
+ private val taskbarMarginLeft: Int,
+ private val taskbarMarginBottom: Int
+) {
+
+ fun calculatePosition(
+ currentRotation: Int,
+ taskbarVisible: Boolean,
+ taskbarStashed: Boolean
+ ): Position {
+
+ val isTaskbarSide = currentRotation == Surface.ROTATION_0
+ || currentRotation == Surface.ROTATION_90
+ val useTaskbarMargin = isTaskbarSide && taskbarVisible && !taskbarStashed
+
+ val gravity = resolveGravity(currentRotation)
+
+ val marginLeft = if (useTaskbarMargin) taskbarMarginLeft else defaultMargin
+ val marginBottom = if (useTaskbarMargin) taskbarMarginBottom else defaultMargin
+
+ val translationX =
+ if (gravity and Gravity.RIGHT == Gravity.RIGHT) {
+ -marginLeft
+ } else {
+ marginLeft
+ }
+ val translationY =
+ if (gravity and Gravity.BOTTOM == Gravity.BOTTOM) {
+ -marginBottom
+ } else {
+ marginBottom
+ }
+
+ return Position(
+ gravity = gravity,
+ translationX = translationX,
+ translationY = translationY
+ )
+ }
+
+ data class Position(
+ val gravity: Int,
+ val translationX: Int,
+ val translationY: Int
+ )
+
+ private fun resolveGravity(rotation: Int): Int =
+ when (rotation) {
+ Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT
+ Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT
+ Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT
+ Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT
+ else -> throw IllegalArgumentException("Invalid rotation $rotation")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index a888305cc83d..625265485f6d 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -54,6 +54,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.Arrays;
+import java.util.Optional;
import java.util.concurrent.Future;
import javax.inject.Inject;
@@ -108,15 +109,15 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
private IThermalEventListener mUsbThermalEventListener;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
@Inject
public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
- CommandQueue commandQueue, Lazy<StatusBar> statusBarLazy) {
+ CommandQueue commandQueue, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
super(context);
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
public void start() {
@@ -710,7 +711,8 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- if (!mStatusBarLazy.get().isDeviceInVrMode()) {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (!statusBarOptional.map(StatusBar::isDeviceInVrMode).orElse(false)) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index 1d2e74703b42..eec69f98b9be 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e9b19e5cfa6f..eeae652eb408 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -160,8 +160,8 @@ public class QSContainerImpl extends FrameLayout {
QuickStatusBarHeaderController quickStatusBarHeaderController) {
mQSPanelContainer.setPaddingRelative(
getPaddingStart(),
- mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height),
+ mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height),
getPaddingEnd(),
getPaddingBottom()
);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 03a2c843a15e..cd7ad1672af4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -57,9 +57,8 @@ public class QuickStatusBarHeader extends FrameLayout {
protected QuickQSPanel mHeaderQsPanel;
private View mDatePrivacyView;
- private View mDateView;
private View mSecurityHeaderView;
- private View mClockIconsView;
+ private View mStatusIconsView;
private View mContainer;
private View mQSCarriers;
@@ -82,7 +81,6 @@ public class QuickStatusBarHeader extends FrameLayout {
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mViewAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
@@ -115,12 +113,11 @@ public class QuickStatusBarHeader extends FrameLayout {
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
- mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+ mStatusIconsView = findViewById(R.id.quick_qs_status_icons);
mQSCarriers = findViewById(R.id.carrier_group);
mContainer = findViewById(R.id.qs_container);
mIconContainer = findViewById(R.id.statusIcons);
mPrivacyChip = findViewById(R.id.privacy_chip);
- mDateView = findViewById(R.id.date);
mSecurityHeaderView = findViewById(R.id.header_text_container);
mClockIconsSeparator = findViewById(R.id.separator);
mRightLayout = findViewById(R.id.rightLayout);
@@ -161,10 +158,6 @@ public class QuickStatusBarHeader extends FrameLayout {
updateAnimators();
}
- public QuickQSPanel getHeaderQsPanel() {
- return mHeaderQsPanel;
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -201,6 +194,11 @@ public class QuickStatusBarHeader extends FrameLayout {
void updateResources() {
Resources resources = mContext.getResources();
+ // status bar is already displayed out of QS in split shade
+ boolean shouldUseSplitShade =
+ resources.getBoolean(R.bool.config_use_split_notification_shade);
+ mStatusIconsView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
+ mDatePrivacyView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
mRoundedCornerPadding = resources.getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
@@ -212,13 +210,13 @@ public class QuickStatusBarHeader extends FrameLayout {
Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
- mClockIconsView.getLayoutParams().height =
- Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
- mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
+ mStatusIconsView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mStatusIconsView.getMinimumHeight());
+ mStatusIconsView.setLayoutParams(mStatusIconsView.getLayoutParams());
ViewGroup.LayoutParams lp = getLayoutParams();
if (mQsDisabled) {
- lp.height = mClockIconsView.getLayoutParams().height;
+ lp.height = mStatusIconsView.getLayoutParams().height;
} else {
lp.height = WRAP_CONTENT;
}
@@ -364,7 +362,7 @@ public class QuickStatusBarHeader extends FrameLayout {
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled);
- mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+ mStatusIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
updateResources();
}
@@ -378,7 +376,7 @@ public class QuickStatusBarHeader extends FrameLayout {
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
- mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
+ mStatusIconsView.setPadding(padding.first, 0, padding.second, 0);
LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
(LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
@@ -440,7 +438,7 @@ public class QuickStatusBarHeader extends FrameLayout {
private void updateHeadersPadding() {
setContentMargins(mDatePrivacyView, 0, 0);
- setContentMargins(mClockIconsView, 0, 0);
+ setContentMargins(mStatusIconsView, 0, 0);
int paddingLeft = 0;
int paddingRight = 0;
@@ -466,7 +464,7 @@ public class QuickStatusBarHeader extends FrameLayout {
mWaterfallTopInset,
paddingRight,
0);
- mClockIconsView.setPadding(paddingLeft,
+ mStatusIconsView.setPadding(paddingLeft,
mWaterfallTopInset,
paddingRight,
0);
@@ -493,7 +491,7 @@ public class QuickStatusBarHeader extends FrameLayout {
* @param scrollY the scroll of the QSPanel container
*/
public void setExpandedScrollAmount(int scrollY) {
- mClockIconsView.setScrollY(scrollY);
+ mStatusIconsView.setScrollY(scrollY);
mDatePrivacyView.setScrollY(scrollY);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7518b200c7e2..d33982c6e172 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -84,8 +84,8 @@ public class QSCustomizer extends LinearLayout {
void updateResources() {
LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
- lp.height = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
+ lp.height = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height);
mTransparentView.setLayoutParams(lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b13015361cc..04f089d31664 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -150,11 +150,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
}
List<CastDevice> activeDevices = getActiveDevices();
- // We want to pop up the media route selection dialog if we either have no active devices
- // (neither routes nor projection), or if we have an active route. In other cases, we assume
- // that a projection is active. This is messy, but this tile never correctly handled the
- // case where multiple devices were active :-/.
- if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) {
+ if (willPopDetail()) {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
showDetail(true);
});
@@ -163,6 +159,15 @@ public class CastTile extends QSTileImpl<BooleanState> {
}
}
+ // We want to pop up the media route selection dialog if we either have no active devices
+ // (neither routes nor projection), or if we have an active route. In other cases, we assume
+ // that a projection is active. This is messy, but this tile never correctly handled the
+ // case where multiple devices were active :-/.
+ private boolean willPopDetail() {
+ List<CastDevice> activeDevices = getActiveDevices();
+ return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+ }
+
private List<CastDevice> getActiveDevices() {
ArrayList<CastDevice> activeDevices = new ArrayList<>();
for (CastDevice device : mController.getCastDevices()) {
@@ -234,10 +239,12 @@ public class CastTile extends QSTileImpl<BooleanState> {
state.contentDescription = state.contentDescription + ","
+ mContext.getString(R.string.accessibility_quick_settings_open_details);
state.expandedAccessibilityClassName = Button.class.getName();
+ state.forceExpandIcon = willPopDetail();
} else {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
state.secondaryLabel = noWifi;
+ state.forceExpandIcon = false;
}
state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
mDetailAdapter.updateItems(devices);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 7cb1421e3f0f..c24b5ada71c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -51,6 +51,7 @@ import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
@@ -66,8 +67,6 @@ import javax.inject.Inject;
/** Quick settings tile: Internet **/
public class InternetTile extends QSTileImpl<SignalState> {
private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
- private static final Intent INTERNET_PANEL =
- new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
protected final NetworkController mController;
private final DataUsageController mDataController;
@@ -75,6 +74,8 @@ public class InternetTile extends QSTileImpl<SignalState> {
private int mLastTileState = -1;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
+ private final InternetDialogFactory mInternetDialogFactory;
+ final Handler mHandler;
@Inject
public InternetTile(
@@ -86,10 +87,13 @@ public class InternetTile extends QSTileImpl<SignalState> {
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- NetworkController networkController
+ NetworkController networkController,
+ InternetDialogFactory internetDialogFactory
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
+ mInternetDialogFactory = internetDialogFactory;
+ mHandler = mainHandler;
mController = networkController;
mDataController = mController.getMobileDataController();
mController.observe(getLifecycle(), mSignalCallback);
@@ -114,7 +118,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Override
protected void handleClick(@Nullable View view) {
- mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
+ mHandler.post(() -> {
+ mInternetDialogFactory.create(true);
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
new file mode 100644
index 000000000000..44c1b7bd7332
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -0,0 +1,225 @@
+/*
+ * 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.qs.tiles.dialog;
+
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE;
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_OWE;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * Adapter for showing Wi-Fi networks.
+ */
+public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
+
+ private static final String TAG = "InternetAdapter";
+ private static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
+ private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+ private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternetDialogController mInternetDialogController;
+
+ protected View mHolderView;
+ protected Context mContext;
+
+ public InternetAdapter(InternetDialogController controller) {
+ mInternetDialogController = controller;
+ }
+
+ @Override
+ public InternetViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+ int viewType) {
+ mContext = viewGroup.getContext();
+ mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
+ viewGroup, false);
+ return new InternetViewHolder(mHolderView, mInternetDialogController);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull InternetViewHolder viewHolder, int position) {
+ List<WifiEntry> wifiList = getWifiEntryList();
+ if (wifiList != null && wifiList.size() != 0) {
+ int count = getItemCount();
+ if (wifiList.size() > count) {
+ wifiList = getWifiEntryList().subList(0, count - 1);
+ }
+
+ if (position < wifiList.size()) {
+ viewHolder.onBind(wifiList.get(position));
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "onBindViewHolder, Wi-Fi entry list = null");
+ }
+ }
+
+ private List<WifiEntry> getWifiEntryList() {
+ if (mInternetDialogController.getWifiEntryList() == null) {
+ return null;
+ }
+
+ return mInternetDialogController.getWifiEntryList().stream()
+ .filter(wifiAp -> wifiAp.getConnectedState()
+ != WifiEntry.CONNECTED_STATE_CONNECTED)
+ .limit(getItemCount())
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * The total number of networks (mobile network and entries of Wi-Fi) should be four in
+ * {@link InternetDialog}.
+ *
+ * Airplane mode is ON (mobile network is gone):
+ * Return four Wi-Fi's entries if no connected Wi-Fi.
+ * Return three Wi-Fi's entries if one connected Wi-Fi.
+ * Airplane mode is OFF (mobile network is visible):
+ * Return three Wi-Fi's entries if no connected Wi-Fi.
+ * Return two Wi-Fi's entries if one connected Wi-Fi.
+ *
+ * @return The total number of networks.
+ */
+ @Override
+ public int getItemCount() {
+ boolean hasConnectedWifi = mInternetDialogController.getConnectedWifiEntry() != null;
+ if (mInternetDialogController.isAirplaneModeEnabled()) {
+ return hasConnectedWifi ? 3 : 4;
+ } else {
+ return hasConnectedWifi ? 2 : 3;
+ }
+ }
+
+ /**
+ * ViewHolder for binding Wi-Fi view.
+ */
+ static class InternetViewHolder extends RecyclerView.ViewHolder {
+
+ final LinearLayout mContainerLayout;
+ final LinearLayout mWifiListLayout;
+ final LinearLayout mWifiNetworkLayout;
+ final ImageView mWifiIcon;
+ final TextView mWifiTitleText;
+ final TextView mWifiSummaryText;
+ final ImageView mWifiLockedIcon;
+ final Context mContext;
+ final InternetDialogController mInternetDialogController;
+
+ InternetViewHolder(View view, InternetDialogController internetDialogController) {
+ super(view);
+ mContext = view.getContext();
+ mInternetDialogController = internetDialogController;
+ mContainerLayout = view.requireViewById(R.id.internet_container);
+ mWifiListLayout = view.requireViewById(R.id.wifi_list);
+ mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
+ mWifiIcon = view.requireViewById(R.id.wifi_icon);
+ mWifiTitleText = view.requireViewById(R.id.wifi_title);
+ mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
+ mWifiLockedIcon = view.requireViewById(R.id.wifi_locked_icon);
+ }
+
+ void onBind(WifiEntry wifiEntry) {
+ int security = wifiEntry.getSecurity();
+ try {
+ mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+ if (isOpenNetwork(security)) {
+ mWifiLockedIcon.setVisibility(View.GONE);
+ } else {
+ mWifiLockedIcon.setVisibility(View.VISIBLE);
+ mWifiLockedIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.ic_friction_lock_closed));
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+
+ setWifiNetworkLayout(wifiEntry.getTitle(),
+ Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
+
+ mWifiListLayout.setOnClickListener(v -> {
+ if (wifiEntry.shouldEditBeforeConnect()) {
+ final Intent intent = new Intent(ACTION_WIFI_DIALOG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, wifiEntry.getKey());
+ intent.putExtra(EXTRA_CONNECT_FOR_CALLER, false);
+ mContext.startActivity(intent);
+ }
+ mInternetDialogController.connect(wifiEntry);
+ });
+ }
+
+ /** Return true if this is an open network AccessPoint. */
+ boolean isOpenNetwork(int security) {
+ return security == SECURITY_NONE
+ || security == SECURITY_OWE;
+ }
+
+ void setWifiNetworkLayout(CharSequence title, CharSequence summary) {
+ mWifiNetworkLayout.setVisibility(View.VISIBLE);
+ mWifiTitleText.setText(title);
+ if (TextUtils.isEmpty(summary)) {
+ mWifiTitleText.setGravity(Gravity.CENTER);
+ mWifiSummaryText.setVisibility(View.GONE);
+ return;
+ } else {
+ mWifiTitleText.setGravity(Gravity.BOTTOM);
+ mWifiSummaryText.setGravity(Gravity.TOP);
+ mWifiSummaryText.setVisibility(View.VISIBLE);
+ }
+ mWifiSummaryText.setText(summary);
+ }
+
+ Drawable getWifiDrawable(WifiEntry wifiEntry) throws Throwable {
+ Drawable drawable = mContext.getDrawable(
+ com.android.internal.R.drawable.ic_wifi_signal_0);
+
+ AtomicReference<Drawable> shared = new AtomicReference<>();
+ final @ColorInt int tint = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.colorControlNormal);
+ Drawable signalDrawable = mContext.getDrawable(
+ Utils.getWifiIconResource(wifiEntry.getLevel()));
+ signalDrawable.setTint(tint);
+ shared.set(signalDrawable);
+ drawable = shared.get();
+ return drawable;
+ }
+ }
+}
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
new file mode 100644
index 000000000000..50fb66c10f83
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -0,0 +1,553 @@
+/*
+ * 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.qs.tiles.dialog;
+
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+
+import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.Space;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
+ */
+@SysUISingleton
+public class InternetDialog extends SystemUIDialog implements
+ InternetDialogController.InternetDialogCallback, Window.Callback {
+ private static final String TAG = "InternetDialog";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final Handler mHandler;
+ private final LinearLayoutManager mLayoutManager;
+ private final Runnable mHideProgressBarRunnable = () -> {
+ setProgressBarVisible(false);
+ };
+
+ @VisibleForTesting
+ protected InternetAdapter mAdapter;
+ @VisibleForTesting
+ protected WifiManager mWifiManager;
+ @VisibleForTesting
+ protected View mDialogView;
+
+ private InternetDialogFactory mInternetDialogFactory;
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyManager mTelephonyManager;
+ private AlertDialog mAlertDialog;
+ private UiEventLogger mUiEventLogger;
+ private Context mContext;
+ private InternetDialogController mInternetDialogController;
+ private TextView mInternetDialogTitle;
+ private TextView mInternetDialogSubTitle;
+ private ProgressBar mProgressBar;
+ private LinearLayout mInternetListLayout;
+ private LinearLayout mConnectedWifListLayout;
+ private LinearLayout mConnectedWifList;
+ private LinearLayout mMobileNetworkLayout;
+ private LinearLayout mMobileNetworkList;
+ private LinearLayout mTurnWifiOnLayout;
+ private LinearLayout mSeeAllLayout;
+ private Space mSpace;
+ private RecyclerView mWifiRecyclerView;
+ private ImageView mConnectedWifiIcon;
+ private ImageView mWifiSettingsIcon;
+ private TextView mConnectedWifiTitleText;
+ private TextView mConnectedWifiSummaryText;
+ private ImageView mSignalIcon;
+ private TextView mMobileTitleText;
+ private TextView mMobileSummaryText;
+ private Switch mMobileDataToggle;
+ private Switch mWiFiToggle;
+ private Button mDoneButton;
+ private Drawable mBackgroundOn;
+ private WifiEntry mConnectedWifiEntry;
+ private int mListMaxHeight;
+ private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private boolean mIsProgressBarVisible;
+
+ private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> {
+ // Set max height for list
+ if (mInternetListLayout.getHeight() > mListMaxHeight) {
+ ViewGroup.LayoutParams params = mInternetListLayout.getLayoutParams();
+ params.height = mListMaxHeight;
+ mInternetListLayout.setLayoutParams(params);
+ }
+ };
+
+ public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
+ InternetDialogController internetDialogController,
+ boolean aboveStatusBar, UiEventLogger uiEventLogger, @Main Handler handler) {
+ super(context, R.style.Theme_SystemUI_Dialog_Internet);
+ if (DEBUG) {
+ Log.d(TAG, "Init InternetDialog");
+ }
+ mContext = context;
+ mHandler = handler;
+ mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogController = internetDialogController;
+ mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
+ mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
+ mTelephonyManager = mInternetDialogController.getTelephonyManager();
+ mWifiManager = mInternetDialogController.getWifiManager();
+
+ mLayoutManager = new LinearLayoutManager(mContext) {
+ @Override
+ public boolean canScrollVertically() {
+ return false;
+ }
+ };
+ mListMaxHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.internet_dialog_list_max_height);
+ mUiEventLogger = uiEventLogger;
+ mAdapter = new InternetAdapter(mInternetDialogController);
+ if (!aboveStatusBar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (DEBUG) {
+ Log.d(TAG, "onCreate");
+ }
+ mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
+ mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
+ null);
+ final Window window = getWindow();
+ final WindowManager.LayoutParams layoutParams = window.getAttributes();
+ layoutParams.gravity = Gravity.BOTTOM;
+ // Move down the dialog to overlay the navigation bar.
+ layoutParams.setFitInsetsTypes(
+ layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars());
+ layoutParams.setFitInsetsSides(WindowInsets.Side.all());
+ layoutParams.setFitInsetsIgnoringVisibility(true);
+ window.setAttributes(layoutParams);
+ window.setContentView(mDialogView);
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ window.setWindowAnimations(R.style.Animation_InternetDialog);
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ window.addFlags(FLAG_LAYOUT_NO_LIMITS);
+
+ mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
+ mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+ mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
+ mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+ mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+ mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
+ mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+ mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
+ mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
+ mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
+ mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
+ mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
+ mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
+ mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
+ mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
+ mSpace = mDialogView.requireViewById(R.id.space);
+ mDoneButton = mDialogView.requireViewById(R.id.done);
+ mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
+ mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
+ mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+ mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
+ mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
+ mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mInternetListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+ mInternetListLayoutListener);
+ mInternetDialogTitle.setText(getDialogTitleText());
+ mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+
+ setOnClickListener();
+ mTurnWifiOnLayout.setBackground(null);
+ mWifiRecyclerView.setLayoutManager(mLayoutManager);
+ mWifiRecyclerView.setAdapter(mAdapter);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (DEBUG) {
+ Log.d(TAG, "onStart");
+ }
+ mInternetDialogController.onStart(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (DEBUG) {
+ Log.d(TAG, "onStop");
+ }
+ mHandler.removeCallbacks(mHideProgressBarRunnable);
+ mMobileNetworkLayout.setOnClickListener(null);
+ mMobileDataToggle.setOnCheckedChangeListener(null);
+ mConnectedWifListLayout.setOnClickListener(null);
+ mSeeAllLayout.setOnClickListener(null);
+ mWiFiToggle.setOnCheckedChangeListener(null);
+ mDoneButton.setOnClickListener(null);
+ mInternetDialogController.onStop();
+ mInternetDialogFactory.destroyDialog();
+ }
+
+ @Override
+ public void dismissDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "dismissDialog");
+ }
+ mInternetDialogFactory.destroyDialog();
+ dismiss();
+ }
+
+ void updateDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "updateDialog");
+ }
+ if (mInternetDialogController.isAirplaneModeEnabled()) {
+ mInternetDialogSubTitle.setVisibility(View.GONE);
+ } else {
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ }
+ showProgressBar();
+ setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
+ setConnectedWifiLayout();
+ boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+ mWiFiToggle.setChecked(isWifiEnabled);
+ int visible = isWifiEnabled ? View.VISIBLE : View.GONE;
+ mWifiRecyclerView.setVisibility(visible);
+ mAdapter.notifyDataSetChanged();
+ mSeeAllLayout.setVisibility(visible);
+ mSpace.setVisibility(isWifiEnabled ? View.GONE : View.VISIBLE);
+ }
+
+ private void setOnClickListener() {
+ mMobileNetworkLayout.setOnClickListener(v -> {
+ if (mInternetDialogController.isMobileDataEnabled()) {
+ if (!mInternetDialogController.activeNetworkIsCellular()) {
+ mInternetDialogController.connectCarrierNetwork();
+ }
+ }
+ });
+ mMobileDataToggle.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> {
+ if (!isChecked && shouldShowMobileDialog()) {
+ showTurnOffMobileDialog();
+ } else if (!shouldShowMobileDialog()) {
+ mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
+ isChecked, false);
+ }
+ });
+ mConnectedWifListLayout.setOnClickListener(v -> {
+ // TODO(b/191475923): Need to launch the detailed page of Wi-Fi entry.
+ });
+ mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
+ mWiFiToggle.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> {
+ buttonView.setChecked(isChecked);
+ mWifiManager.setWifiEnabled(isChecked);
+ mSpace.setVisibility(isChecked ? View.GONE : View.VISIBLE);
+ });
+ mDoneButton.setOnClickListener(v -> dismiss());
+ }
+
+ private void setMobileDataLayout(boolean isCellularNetwork) {
+ if (mInternetDialogController.isAirplaneModeEnabled()
+ || !mInternetDialogController.hasCarrier()) {
+ mMobileNetworkLayout.setVisibility(View.GONE);
+ } else {
+ mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
+ mMobileNetworkLayout.setVisibility(View.VISIBLE);
+ mMobileTitleText.setText(getMobileNetworkTitle());
+ mMobileSummaryText.setText(
+ Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
+ mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
+ int titleColor = isCellularNetwork ? mContext.getColor(
+ R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+ mContext, android.R.attr.textColorPrimary);
+ int summaryColor = isCellularNetwork ? mContext.getColor(
+ R.color.connected_network_tertiary_color) : Utils.getColorAttrDefaultColor(
+ mContext, android.R.attr.textColorTertiary);
+ mMobileTitleText.setTextColor(titleColor);
+ mMobileSummaryText.setTextColor(summaryColor);
+ mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
+ }
+ }
+
+ private void setConnectedWifiLayout() {
+ if (!mWifiManager.isWifiEnabled()
+ || mInternetDialogController.getConnectedWifiEntry() == null) {
+ mConnectedWifListLayout.setBackground(null);
+ mConnectedWifListLayout.setVisibility(View.GONE);
+ return;
+ }
+ mConnectedWifListLayout.setVisibility(View.VISIBLE);
+ mConnectedWifiTitleText.setText(getConnectedWifiTitle());
+ mConnectedWifiSummaryText.setText(getConnectedWifiSummary());
+ mConnectedWifiIcon.setImageDrawable(getConnectedWifiDrawable());
+ mConnectedWifiTitleText.setTextColor(
+ mContext.getColor(R.color.connected_network_primary_color));
+ mConnectedWifiSummaryText.setTextColor(
+ mContext.getColor(R.color.connected_network_tertiary_color));
+ mWifiSettingsIcon.setColorFilter(
+ mContext.getColor(R.color.connected_network_primary_color));
+ mConnectedWifListLayout.setBackground(mBackgroundOn);
+ }
+
+ void onClickSeeMoreButton() {
+ mInternetDialogController.launchNetworkSetting();
+ }
+
+ CharSequence getDialogTitleText() {
+ return mInternetDialogController.getDialogTitleText();
+ }
+
+ CharSequence getSubtitleText() {
+ return mInternetDialogController.getSubtitleText(mIsProgressBarVisible);
+ }
+
+ private Drawable getConnectedWifiDrawable() {
+ try {
+ return mInternetDialogController.getWifiConnectedDrawable(mConnectedWifiEntry);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private Drawable getSignalStrengthDrawable() {
+ return mInternetDialogController.getSignalStrengthDrawable();
+ }
+
+ CharSequence getMobileNetworkTitle() {
+ return mInternetDialogController.getMobileNetworkTitle();
+ }
+
+ String getMobileNetworkSummary() {
+ return mInternetDialogController.getMobileNetworkSummary();
+ }
+
+ String getConnectedWifiTitle() {
+ return mInternetDialogController.getConnectedWifiTitle();
+ }
+
+ String getConnectedWifiSummary() {
+ return mInternetDialogController.getConnectedWifiSummary();
+ }
+
+ private void showProgressBar() {
+ if (mWifiManager == null || !mWifiManager.isWifiEnabled()) {
+ setProgressBarVisible(false);
+ return;
+ }
+ setProgressBarVisible(true);
+ List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
+ if (wifiScanResults != null && wifiScanResults.size() > 0) {
+ mContext.getMainThreadHandler().postDelayed(mHideProgressBarRunnable,
+ 2000 /* delay millis */);
+ }
+ }
+
+ private void setProgressBarVisible(boolean visible) {
+ if (mWifiManager.isWifiEnabled() && mAdapter.mHolderView != null
+ && mAdapter.mHolderView.isAttachedToWindow()) {
+ mIsProgressBarVisible = true;
+ }
+ mIsProgressBarVisible = visible;
+ mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.INVISIBLE);
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ }
+
+ private boolean shouldShowMobileDialog() {
+ boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
+ false);
+ if (mInternetDialogController.isMobileDataEnabled() && !flag) {
+ return true;
+ }
+ return false;
+ }
+
+ private void showTurnOffMobileDialog() {
+ CharSequence carrierName =
+ mSubscriptionManager.getDefaultDataSubscriptionInfo().getCarrierName();
+ boolean isInService = mInternetDialogController.isVoiceStateInService();
+ if (TextUtils.isEmpty(carrierName) || !isInService) {
+ carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ }
+ mAlertDialog = new Builder(mContext)
+ .setTitle(R.string.mobile_data_disable_title)
+ .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+ .setNegativeButton(android.R.string.cancel, (d, w) -> {
+ mMobileDataToggle.setChecked(true);
+ })
+ .setPositiveButton(
+ com.android.internal.R.string.alert_windows_notification_turn_off_action,
+ (d, w) -> {
+ mInternetDialogController.setMobileDataEnabled(mContext,
+ mDefaultDataSubId, false, false);
+ mMobileDataToggle.setChecked(false);
+ Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+ })
+ .create();
+ mAlertDialog.setOnCancelListener(dialog -> mMobileDataToggle.setChecked(true));
+ mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
+ SystemUIDialog.registerDismissListener(mAlertDialog);
+ SystemUIDialog.setWindowOnTop(mAlertDialog);
+ mAlertDialog.show();
+ }
+
+ @Override
+ public void onRefreshCarrierInfo() {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onSimStateChanged() {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onSubscriptionsChanged(int defaultDataSubId) {
+ mDefaultDataSubId = defaultDataSubId;
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mAdapter.notifyDataSetChanged();
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry) {
+ mConnectedWifiEntry = connectedEntry;
+ mAdapter.notifyDataSetChanged();
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (mAlertDialog != null && !mAlertDialog.isShowing()) {
+ if (!hasFocus && isShowing()) {
+ dismiss();
+ }
+ }
+ }
+
+ @Override
+ public void onWifiStateReceived(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+
+ String action = intent.getAction();
+ if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ mInternetDialogController.scanWifiAccessPoints();
+ showProgressBar();
+ return;
+ }
+
+ if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mHandler.post(() -> updateDialog());
+ }
+ }
+
+ public enum InternetDialogEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The Internet dialog became visible on the screen.")
+ INTERNET_DIALOG_SHOW(843);
+
+ private final int mId;
+
+ InternetDialogEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
new file mode 100644
index 000000000000..24c2fb1eb3f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -0,0 +1,851 @@
+/*
+ * 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.qs.tiles.dialog;
+
+import static com.android.settingslib.mobile.MobileMappings.getIconKey;
+import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+
+import android.annotation.ColorInt;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.widget.Toast;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.Utils;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.MobileMappings;
+import com.android.settingslib.net.SignalStrengthUtil;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.wifitrackerlib.MergedCarrierEntry;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+public class InternetDialogController implements WifiEntry.DisconnectCallback,
+ NetworkController.AccessPointController.AccessPointCallback {
+
+ private static final String TAG = "InternetDialogController";
+ private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+ "android.settings.NETWORK_PROVIDER_SETTINGS";
+ private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+ public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
+ public static final int NO_CELL_DATA_TYPE_ICON = 0;
+ private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
+ private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
+ R.string.tap_a_network_to_connect;
+ private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
+ R.string.wifi_empty_list_wifi_on;
+ private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
+ R.string.non_carrier_network_unavailable;
+ private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
+ R.string.all_network_unavailable;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private WifiManager mWifiManager;
+ private Context mContext;
+ private ActivityStarter mActivityStarter;
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyManager mTelephonyManager;
+ private ConnectivityManager mConnectivityManager;
+ private TelephonyDisplayInfo mTelephonyDisplayInfo =
+ new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+ private Handler mHandler;
+ private MobileMappings.Config mConfig = null;
+ private Executor mExecutor;
+ private AccessPointController mAccessPointController;
+ private IntentFilter mWifiStateFilter;
+ private InternetDialogCallback mCallback;
+ private List<WifiEntry> mWifiEntry;
+ private WifiEntry mConnectedEntry;
+ private UiEventLogger mUiEventLogger;
+ private BroadcastDispatcher mBroadcastDispatcher;
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private GlobalSettings mGlobalSettings;
+ private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ @VisibleForTesting
+ protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
+ @VisibleForTesting
+ protected InternetTelephonyCallback mInternetTelephonyCallback;
+
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshCarrierInfo() {
+ mCallback.onRefreshCarrierInfo();
+ }
+
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ mCallback.onSimStateChanged();
+ }
+ };
+
+ protected List<SubscriptionInfo> getSubscriptionInfo() {
+ return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
+ }
+
+ @Inject
+ public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
+ ActivityStarter starter, AccessPointController accessPointController,
+ SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+ @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+ @Main Handler handler, @Main Executor mainExecutor,
+ BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ GlobalSettings globalSettings) {
+ if (DEBUG) {
+ Log.d(TAG, "Init InternetDialogController");
+ }
+ mHandler = handler;
+ mExecutor = mainExecutor;
+ mContext = context;
+ mGlobalSettings = globalSettings;
+ mWifiManager = wifiManager;
+ mTelephonyManager = telephonyManager;
+ mConnectivityManager = connectivityManager;
+ mSubscriptionManager = subscriptionManager;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mWifiStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ mUiEventLogger = uiEventLogger;
+ mActivityStarter = starter;
+ mAccessPointController = accessPointController;
+ mConfig = MobileMappings.Config.readConfig(mContext);
+ }
+
+ void onStart(@NonNull InternetDialogCallback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "onStart");
+ }
+ mCallback = callback;
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ mAccessPointController.addAccessPointCallback(this);
+ mBroadcastDispatcher.registerReceiver(mWifiStateReceiver, mWifiStateFilter, mExecutor);
+ // Listen the subscription changes
+ mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+ mOnSubscriptionsChangedListener);
+ mDefaultDataSubId = getDefaultDataSubscriptionId();
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mInternetTelephonyCallback = new InternetTelephonyCallback();
+ mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
+ // Listen the connectivity changes
+ mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), new DataConnectivityListener(), mHandler);
+ scanWifiAccessPoints();
+ }
+
+ void onStop() {
+ if (DEBUG) {
+ Log.d(TAG, "onStop");
+ }
+ mBroadcastDispatcher.unregisterReceiver(mWifiStateReceiver);
+ mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ mAccessPointController.removeAccessPointCallback(this);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ }
+
+ @VisibleForTesting
+ boolean isAirplaneModeEnabled() {
+ return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+
+ @VisibleForTesting
+ protected int getDefaultDataSubscriptionId() {
+ return mSubscriptionManager.getDefaultDataSubscriptionId();
+ }
+
+ @VisibleForTesting
+ protected Intent getSettingsIntent() {
+ return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ CharSequence getDialogTitleText() {
+ if (isAirplaneModeEnabled()) {
+ return mContext.getText(R.string.airplane_mode);
+ }
+ return mContext.getText(R.string.quick_settings_internet_label);
+ }
+
+ CharSequence getSubtitleText(boolean isProgressBarVisible) {
+ if (isAirplaneModeEnabled()) {
+ return null;
+ }
+
+ if (!mWifiManager.isWifiEnabled()) {
+ // When the airplane mode is off and Wi-Fi is disabled.
+ // Sub-Title: Wi-Fi is off
+ if (DEBUG) {
+ Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
+ }
+
+ if (isProgressBarVisible) {
+ // When the Wi-Fi scan result callback is received
+ // Sub-Title: Searching for networks...
+ return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
+ }
+
+ final List<ScanResult> wifiList = mWifiManager.getScanResults();
+ if (wifiList != null && wifiList.size() != 0) {
+ return mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT);
+ }
+
+ // Sub-Title:
+ // show non_carrier_network_unavailable
+ // - while Wi-Fi on + no Wi-Fi item
+ // - while Wi-Fi on + no Wi-Fi item + mobile data off
+ // show all_network_unavailable:
+ // - while Wi-Fi on + no Wi-Fi item + no carrier item
+ // - while Wi-Fi on + no Wi-Fi item + service is out of service
+ // - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
+ if (DEBUG) {
+ Log.d(TAG, "No Wi-Fi item.");
+ }
+ if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
+ if (DEBUG) {
+ Log.d(TAG, "No carrier or service is out of service.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ if (!isMobileDataEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "Mobile data off");
+ }
+ return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ if (!activeNetworkIsCellular()) {
+ if (DEBUG) {
+ Log.d(TAG, "No carrier data.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ Drawable getWifiConnectedDrawable(WifiEntry wifiEntry) throws Throwable {
+ final @ColorInt int tint;
+ tint = Utils.getColorAttrDefaultColor(mContext,
+ com.android.internal.R.attr.colorAccentPrimaryVariant);
+ final Drawable drawable = mContext.getDrawable(
+ com.android.settingslib.Utils.getWifiIconResource(wifiEntry.getLevel()));
+ drawable.setTint(tint);
+
+ return drawable;
+ }
+
+ Drawable getSignalStrengthDrawable() {
+ Drawable drawable = mContext.getDrawable(
+ R.drawable.ic_signal_strength_zero_bar_no_internet);
+ try {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null");
+ }
+ return drawable;
+ }
+
+ if (isDataStateInService() || isVoiceStateInService()) {
+ AtomicReference<Drawable> shared = new AtomicReference<>();
+ shared.set(getSignalStrengthDrawableWithLevel());
+ drawable = shared.get();
+ }
+
+ drawable.setTint(
+ Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal));
+ if (activeNetworkIsCellular()) {
+ drawable.setTint(Utils.getColorAttrDefaultColor(mContext,
+ com.android.internal.R.attr.colorAccentPrimaryVariant));
+ }
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ return drawable;
+ }
+
+ /**
+ * To get the signal bar icon with level.
+ *
+ * @return The Drawable which is a signal bar icon with level.
+ */
+ Drawable getSignalStrengthDrawableWithLevel() {
+ final SignalStrength strength = mTelephonyManager.getSignalStrength();
+ int level = (strength == null) ? 0 : strength.getLevel();
+ int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+ if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
+ level += 1;
+ numLevels += 1;
+ }
+ return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
+ }
+
+ Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
+ int iconType, boolean cutOut) {
+ Log.d(TAG, "getSignalStrengthIcon");
+ final SignalDrawable signalDrawable = new SignalDrawable(context);
+ signalDrawable.setLevel(
+ SignalDrawable.getState(level, numLevels, cutOut));
+
+ // Make the network type drawable
+ final Drawable networkDrawable =
+ iconType == NO_CELL_DATA_TYPE_ICON
+ ? EMPTY_DRAWABLE
+ : context.getResources().getDrawable(iconType, context.getTheme());
+
+ // Overlay the two drawables
+ final Drawable[] layers = {networkDrawable, signalDrawable};
+ final int iconSize =
+ context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
+
+ final LayerDrawable icons = new LayerDrawable(layers);
+ // Set the network type icon at the top left
+ icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
+ // Set the signal strength icon at the bottom right
+ icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
+ icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
+ icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
+ return icons;
+ }
+
+ private boolean shouldInflateSignalStrength(int subId) {
+ return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
+ }
+
+ private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
+ final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
+ return displayNames.getOrDefault(subscriptionId, "");
+ }
+
+ private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
+ class DisplayInfo {
+ public SubscriptionInfo subscriptionInfo;
+ public CharSequence originalName;
+ public CharSequence uniqueName;
+ }
+
+ // Map of SubscriptionId to DisplayName
+ final Supplier<Stream<DisplayInfo>> originalInfos =
+ () -> getSubscriptionInfo()
+ .stream()
+ .filter(i -> {
+ // Filter out null values.
+ return (i != null && i.getDisplayName() != null);
+ })
+ .map(i -> {
+ DisplayInfo info = new DisplayInfo();
+ info.subscriptionInfo = i;
+ info.originalName = i.getDisplayName().toString().trim();
+ return info;
+ });
+
+ // A Unique set of display names
+ Set<CharSequence> uniqueNames = new HashSet<>();
+ // Return the set of duplicate names
+ final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
+ .filter(info -> !uniqueNames.add(info.originalName))
+ .map(info -> info.originalName)
+ .collect(Collectors.toSet());
+
+ // If a display name is duplicate, append the final 4 digits of the phone number.
+ // Creates a mapping of Subscription id to original display name + phone number display name
+ final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+ if (duplicateOriginalNames.contains(info.originalName)) {
+ // This may return null, if the user cannot view the phone number itself.
+ final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
+ info.subscriptionInfo);
+ String lastFourDigits = "";
+ if (phoneNumber != null) {
+ lastFourDigits = (phoneNumber.length() > 4)
+ ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
+ }
+
+ if (TextUtils.isEmpty(lastFourDigits)) {
+ info.uniqueName = info.originalName;
+ } else {
+ info.uniqueName = info.originalName + " " + lastFourDigits;
+ }
+
+ } else {
+ info.uniqueName = info.originalName;
+ }
+ return info;
+ });
+
+ // Check uniqueness a second time.
+ // We might not have had permission to view the phone numbers.
+ // There might also be multiple phone numbers whose last 4 digits the same.
+ uniqueNames.clear();
+ final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
+ .filter(info -> !uniqueNames.add(info.uniqueName))
+ .map(info -> info.uniqueName)
+ .collect(Collectors.toSet());
+
+ return uniqueInfos.get().map(info -> {
+ if (duplicatePhoneNames.contains(info.uniqueName)) {
+ info.uniqueName = info.originalName + " "
+ + info.subscriptionInfo.getSubscriptionId();
+ }
+ return info;
+ }).collect(Collectors.toMap(
+ info -> info.subscriptionInfo.getSubscriptionId(),
+ info -> info.uniqueName));
+ }
+
+ CharSequence getMobileNetworkTitle() {
+ return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
+ }
+
+ String getMobileNetworkSummary() {
+ String description = getNetworkTypeDescription(mContext, mConfig,
+ mTelephonyDisplayInfo, mDefaultDataSubId);
+ return getMobileSummary(mContext, mTelephonyManager, description);
+ }
+
+ /**
+ * Get currently description of mobile network type.
+ */
+ private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
+ TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
+ String iconKey = getIconKey(telephonyDisplayInfo);
+
+ if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
+ if (DEBUG) {
+ Log.d(TAG, "The description of network type is empty.");
+ }
+ return "";
+ }
+
+ int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ return resId != 0
+ ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
+ }
+
+ private String getMobileSummary(Context context, TelephonyManager telephonyManager,
+ String networkTypeDescription) {
+ if (!isMobileDataEnabled()) {
+ return context.getString(R.string.mobile_data_off_summary);
+ }
+ if (!isDataStateInService()) {
+ return context.getString(R.string.mobile_data_no_connection);
+ }
+ String summary = networkTypeDescription;
+ if (activeNetworkIsCellular()) {
+ summary = context.getString(R.string.preference_summary_default_combination,
+ context.getString(R.string.mobile_data_connection_active),
+ networkTypeDescription);
+ }
+ return summary;
+ }
+
+ String getConnectedWifiTitle() {
+ if (getConnectedWifiEntry() == null) {
+ if (DEBUG) {
+ Log.d(TAG, "connected entry is null");
+ }
+ return "";
+ }
+ return getConnectedWifiEntry().getTitle();
+ }
+
+ String getConnectedWifiSummary() {
+ if (getConnectedWifiEntry() == null) {
+ if (DEBUG) {
+ Log.d(TAG, "connected entry is null");
+ }
+ return "";
+ }
+ return getConnectedWifiEntry().getSummary(false);
+ }
+
+ void launchNetworkSetting() {
+ mCallback.dismissDialog();
+ mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
+ }
+
+ void connectCarrierNetwork() {
+ final MergedCarrierEntry mergedCarrierEntry =
+ mAccessPointController.getMergedCarrierEntry();
+ if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
+ mergedCarrierEntry.connect(null /* ConnectCallback */);
+ }
+ }
+
+ List<WifiEntry> getWifiEntryList() {
+ return mWifiEntry;
+ }
+
+ WifiEntry getConnectedWifiEntry() {
+ return mConnectedEntry;
+ }
+
+ WifiManager getWifiManager() {
+ return mWifiManager;
+ }
+
+ TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ SubscriptionManager getSubscriptionManager() {
+ return mSubscriptionManager;
+ }
+
+ /**
+ * @return whether there is the carrier item in the slice.
+ */
+ boolean hasCarrier() {
+ if (mSubscriptionManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
+ }
+ return false;
+ }
+
+ if (isAirplaneModeEnabled() || mTelephonyManager == null
+ || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return {@code true} if mobile data is enabled
+ */
+ boolean isMobileDataEnabled() {
+ if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set whether to enable data for {@code subId}, also whether to disable data for other
+ * subscription
+ */
+ void setMobileDataEnabled(Context context, int subId, boolean enabled,
+ boolean disableOtherSubscriptions) {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
+ }
+ return;
+ }
+
+ if (mSubscriptionManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
+ }
+ return;
+ }
+
+ mTelephonyManager.setDataEnabled(enabled);
+ if (disableOtherSubscriptions) {
+ final List<SubscriptionInfo> subInfoList =
+ mSubscriptionManager.getActiveSubscriptionInfoList();
+ if (subInfoList != null) {
+ for (SubscriptionInfo subInfo : subInfoList) {
+ // We never disable mobile data for opportunistic subscriptions.
+ if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
+ context.getSystemService(TelephonyManager.class).createForSubscriptionId(
+ subInfo.getSubscriptionId()).setDataEnabled(false);
+ }
+ }
+ }
+ }
+ }
+
+ boolean isDataStateInService() {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null, can not detect mobile state.");
+ }
+ return false;
+ }
+ return mTelephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED;
+ }
+
+ boolean isVoiceStateInService() {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
+ }
+ return false;
+ }
+
+ final ServiceState serviceState = mTelephonyManager.getServiceState();
+ return serviceState != null
+ && serviceState.getState() == serviceState.STATE_IN_SERVICE;
+ }
+
+ boolean activeNetworkIsCellular() {
+ if (mConnectivityManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "ConnectivityManager is null, can not check active network.");
+ }
+ return false;
+ }
+
+ final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+ if (activeNetwork == null) {
+ return false;
+ }
+ final NetworkCapabilities networkCapabilities =
+ mConnectivityManager.getNetworkCapabilities(activeNetwork);
+ if (networkCapabilities == null) {
+ return false;
+ }
+ return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+ }
+
+ boolean connect(WifiEntry ap) {
+ if (ap == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No Wi-Fi ap to connect.");
+ }
+ return false;
+ }
+
+ if (ap.getWifiConfiguration() != null) {
+ if (DEBUG) {
+ Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "connect to unsaved network " + ap.getTitle());
+ }
+ }
+ ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
+ return false;
+ }
+
+ static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
+ final ActivityStarter mActivityStarter;
+ final Context mContext;
+ final WifiEntry mWifiEntry;
+
+ WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
+ WifiEntry connectWifiEntry) {
+ mActivityStarter = activityStarter;
+ mContext = context;
+ mWifiEntry = connectWifiEntry;
+ }
+
+ @Override
+ public void onConnectResult(@ConnectStatus int status) {
+ if (DEBUG) {
+ Log.d(TAG, "onConnectResult " + status);
+ }
+
+ if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
+ final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
+ .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivityStarter.startActivity(intent, true);
+ } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
+ Toast.makeText(mContext, R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "connect failure reason=" + status);
+ }
+ }
+ }
+ }
+
+ void scanWifiAccessPoints() {
+ mAccessPointController.scanForAccessPoints();
+ }
+
+ @Override
+ public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
+ if (accessPoints == null) {
+ return;
+ }
+
+ boolean hasConnectedWifi = false;
+ mWifiEntry = accessPoints;
+ for (WifiEntry wifiEntry : accessPoints) {
+ if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
+ mConnectedEntry = wifiEntry;
+ hasConnectedWifi = true;
+ break;
+ }
+ }
+ if (!hasConnectedWifi) {
+ mConnectedEntry = null;
+ }
+
+ mCallback.onAccessPointsChanged(mWifiEntry, mConnectedEntry);
+ }
+
+ @Override
+ public void onSettingsActivityTriggered(Intent settingsIntent) {
+ }
+
+ @Override
+ public void onDisconnectResult(int status) {
+ }
+
+ private class InternetTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.DataConnectionStateListener,
+ TelephonyCallback.DisplayInfoListener,
+ TelephonyCallback.ServiceStateListener,
+ TelephonyCallback.SignalStrengthsListener {
+
+ @Override
+ public void onServiceStateChanged(@NonNull ServiceState serviceState) {
+ mCallback.onServiceStateChanged(serviceState);
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mCallback.onDataConnectionStateChanged(state, networkType);
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
+ mCallback.onSignalStrengthsChanged(signalStrength);
+ }
+
+ @Override
+ public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+ mTelephonyDisplayInfo = telephonyDisplayInfo;
+ mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
+ }
+ }
+
+ private class InternetOnSubscriptionChangedListener
+ extends SubscriptionManager.OnSubscriptionsChangedListener {
+ InternetOnSubscriptionChangedListener() {
+ super();
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ mDefaultDataSubId = getDefaultDataSubscriptionId();
+ if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
+ mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mTelephonyManager.registerTelephonyCallback(mHandler::post,
+ mInternetTelephonyCallback);
+ mCallback.onSubscriptionsChanged(mDefaultDataSubId);
+ }
+ }
+ }
+
+ private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+ if (activeNetwork != null && activeNetwork.equals(network)) {
+ // update UI
+ mCallback.onCapabilitiesChanged(network, networkCapabilities);
+ }
+ }
+ }
+
+ private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCallback.onWifiStateReceived(context, intent);
+ }
+ };
+
+ interface InternetDialogCallback {
+
+ void onRefreshCarrierInfo();
+
+ void onSimStateChanged();
+
+ void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
+
+ void onSubscriptionsChanged(int defaultDataSubId);
+
+ void onServiceStateChanged(ServiceState serviceState);
+
+ void onDataConnectionStateChanged(int state, int networkType);
+
+ void onSignalStrengthsChanged(SignalStrength signalStrength);
+
+ void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
+
+ void dismissDialog();
+
+ void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry);
+
+ void onWifiStateReceived(Context context, Intent intent);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
new file mode 100644
index 000000000000..85e6160e98c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.qs.tiles.dialog
+
+import android.content.Context
+import android.os.Handler
+import android.util.Log
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+
+private const val TAG = "InternetDialogFactory"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+/**
+ * Factory to create [InternetDialog] objects.
+ */
+@SysUISingleton
+class InternetDialogFactory @Inject constructor(
+ @Main private val handler: Handler,
+ private val internetDialogController: InternetDialogController,
+ private val context: Context,
+ private val uiEventLogger: UiEventLogger
+) {
+ companion object {
+ var internetDialog: InternetDialog? = null
+ }
+
+ /** Creates a [InternetDialog]. */
+ fun create(aboveStatusBar: Boolean) {
+ if (internetDialog != null) {
+ if (DEBUG) {
+ Log.d(TAG, "InternetDialog is showing, do not create it twice.")
+ }
+ return
+ } else {
+ internetDialog = InternetDialog(context, this, internetDialogController, aboveStatusBar,
+ uiEventLogger, handler)
+ internetDialog?.show()
+ }
+ }
+
+ fun destroyDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "destroyDialog")
+ }
+ internetDialog = null
+ }
+}
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
new file mode 100644
index 000000000000..6aaba997faad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
@@ -0,0 +1,14 @@
+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/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index bbeff6ece902..77c61a4f1845 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -42,7 +42,7 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
private final static String TAG = "OverviewProxyRecentsImpl";
@Nullable
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private Context mContext;
private Handler mHandler;
@@ -51,8 +51,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
- mStatusBarLazy = statusBarLazy.orElse(null);
+ public OverviewProxyRecentsImpl(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
@Override
@@ -109,8 +109,9 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
}
};
// Preload only if device for current user is unlocked
- if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
- mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+ statusBarOptional.get().executeRunnableDismissingKeyguard(() -> {
// Flush trustmanager before checking device locked per user
mTrustManager.reportKeyguardShowingChanged();
mHandler.post(toggleRecents);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cb0c411b2753..eb72296d5c5b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -34,6 +34,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUP
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -109,6 +110,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
+import java.util.function.Supplier;
import javax.inject.Inject;
@@ -134,7 +136,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final Context mContext;
private final Optional<Pip> mPipOptional;
- private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
private final Optional<SplitScreen> mSplitScreenOptional;
private SysUiState mSysUiState;
@@ -171,55 +173,33 @@ public class OverviewProxyService extends CurrentUserTracker implements
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@Override
public void startScreenPinning(int taskId) {
- if (!verifyCaller("startScreenPinning")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- mStatusBarOptionalLazy.ifPresent(
- statusBarLazy -> statusBarLazy.get().showScreenPinningRequest(taskId,
- false /* allowCancel */));
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.showScreenPinningRequest(taskId,
+ false /* allowCancel */)));
}
@Override
public void stopScreenPinning() {
- if (!verifyCaller("stopScreenPinning")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- try {
- ActivityTaskManager.getService().stopSystemLockTaskMode();
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to stop screen pinning");
- }
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("stopScreenPinning", () -> {
+ try {
+ ActivityTaskManager.getService().stopSystemLockTaskMode();
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to stop screen pinning");
+ }
+ });
}
// TODO: change the method signature to use (boolean inputFocusTransferStarted)
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
- if (!verifyCaller("onStatusBarMotionEvent")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
+ verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> {
// TODO move this logic to message queue
- mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
- StatusBar statusBar = statusBarLazy.get();
+ mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
if (event.getActionMasked() == ACTION_DOWN) {
statusBar.getPanelController().startExpandLatencyTracking();
}
- mHandler.post(()-> {
+ mHandler.post(() -> {
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
mInputFocusTransferStarted = true;
@@ -231,50 +211,38 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mInputFocusTransferStarted = false;
+ float velocity = (event.getY() - mInputFocusTransferStartY)
+ / (event.getEventTime() - mInputFocusTransferStartMillis);
statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
action == ACTION_CANCEL,
- (event.getY() - mInputFocusTransferStartY)
- / (event.getEventTime() - mInputFocusTransferStartMillis));
+ velocity);
}
event.recycle();
});
});
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ });
}
@Override
public void onBackPressed() throws RemoteException {
- if (!verifyCaller("onBackPressed")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- notifyBackAction(true, -1, -1, true, false);
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ notifyBackAction(true, -1, -1, true, false);
+ });
}
@Override
public void setHomeRotationEnabled(boolean enabled) {
- if (!verifyCaller("setHomeRotationEnabled")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- mHandler.post(() -> notifyHomeRotationEnabled(enabled));
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
+ mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
+ }
+
+ @Override
+ public void notifyTaskbarStatus(boolean visible, boolean stashed) {
+ verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
+ onTaskbarStatusUpdated(visible, stashed));
}
private boolean sendEvent(int action, int code) {
@@ -291,124 +259,74 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void onOverviewShown(boolean fromHome) {
- if (!verifyCaller("onOverviewShown")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onOverviewShown(fromHome);
- }
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("onOverviewShown", () -> {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onOverviewShown(fromHome);
+ }
+ });
}
@Override
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
- if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
- return null;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- return mLegacySplitScreenOptional.map(splitScreen ->
- splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
- .orElse(null);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return verifyCallerAndClearCallingIdentity(
+ "getNonMinimizedSplitScreenSecondaryBounds",
+ () -> mLegacySplitScreenOptional.map(splitScreen ->
+ splitScreen
+ .getDividerView()
+ .getNonMinimizedSplitScreenSecondaryBounds())
+ .orElse(null)
+ );
}
@Override
public void setNavBarButtonAlpha(float alpha, boolean animate) {
- if (!verifyCaller("setNavBarButtonAlpha")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mNavBarButtonAlpha = alpha;
- mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
+ notifyNavBarButtonAlphaChanged(alpha, animate));
}
@Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
- if (!verifyCaller("onAssistantProgress")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyAssistantProgress(progress));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
+ notifyAssistantProgress(progress));
}
@Override
public void onAssistantGestureCompletion(float velocity) {
- if (!verifyCaller("onAssistantGestureCompletion")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("onAssistantGestureCompletion", () ->
+ notifyAssistantGestureCompletion(velocity));
}
@Override
public void startAssistant(Bundle bundle) {
- if (!verifyCaller("startAssistant")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyStartAssistant(bundle));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("startAssistant", () ->
+ notifyStartAssistant(bundle));
}
@Override
public void notifyAccessibilityButtonClicked(int displayId) {
- if (!verifyCaller("notifyAccessibilityButtonClicked")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- AccessibilityManager.getInstance(mContext)
- .notifyAccessibilityButtonClicked(displayId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () ->
+ AccessibilityManager.getInstance(mContext)
+ .notifyAccessibilityButtonClicked(displayId));
}
@Override
public void notifyAccessibilityButtonLongClicked() {
- if (!verifyCaller("notifyAccessibilityButtonLongClicked")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- final Intent intent =
- new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
- intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked",
+ () -> {
+ final Intent intent =
+ new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
+ final String chooserClassName = AccessibilityButtonChooserActivity
+ .class.getName();
+ intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ });
}
@Override
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
- Insets visibleInsets, int taskId) {
+ Insets visibleInsets, int taskId) {
// Deprecated
}
@@ -420,43 +338,22 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void notifySwipeToHomeFinished() {
- if (!verifyCaller("notifySwipeToHomeFinished")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mPipOptional.ifPresent(
- pip -> pip.setPinnedStackAnimationType(
- PipAnimationController.ANIM_TYPE_ALPHA));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () ->
+ mPipOptional.ifPresent(
+ pip -> pip.setPinnedStackAnimationType(
+ PipAnimationController.ANIM_TYPE_ALPHA)));
}
@Override
public void notifySwipeUpGestureStarted() {
- if (!verifyCaller("notifySwipeUpGestureStarted")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifySwipeUpGestureStartedInternal());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () ->
+ notifySwipeUpGestureStartedInternal());
}
@Override
public void notifyPrioritizedRotation(@Surface.Rotation int rotation) {
- if (!verifyCaller("notifyPrioritizedRotation")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyPrioritizedRotationInternal(rotation));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () ->
+ notifyPrioritizedRotationInternal(rotation));
}
@Override
@@ -476,15 +373,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void expandNotificationPanel() {
- if (!verifyCaller("expandNotificationPanel")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("expandNotificationPanel",
+ () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
}
private boolean verifyCaller(String reason) {
@@ -496,6 +386,29 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
return true;
}
+
+ private <T> T verifyCallerAndClearCallingIdentity(String reason, Supplier<T> supplier) {
+ if (!verifyCaller(reason)) {
+ return null;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return supplier.get();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void verifyCallerAndClearCallingIdentity(String reason, Runnable runnable) {
+ verifyCallerAndClearCallingIdentity(reason, () -> {
+ runnable.run();
+ return null;
+ });
+ }
+
+ private void verifyCallerAndClearCallingIdentityPostMain(String reason, Runnable runnable) {
+ verifyCallerAndClearCallingIdentity(reason, () -> mHandler.post(runnable));
+ }
};
private final Runnable mDeferredConnectionCallback = () -> {
@@ -613,7 +526,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> legacySplitScreenOptional,
Optional<SplitScreen> splitScreenOptional,
- Optional<Lazy<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
@@ -736,12 +649,13 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
- boolean bouncerShowing) {
+ boolean bouncerShowing, boolean isDozing) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded)
.setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
+ .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
.commitUpdate(mContext.getDisplayId());
}
@@ -766,10 +680,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
- mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
+ mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
mInputFocusTransferStarted = false;
- statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
- 0 /* velocity */);
+ statusBar.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
});
});
}
@@ -881,6 +794,12 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
+ private void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed);
+ }
+ }
+
private void notifyConnectionChanged() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
@@ -967,19 +886,40 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
- public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
try {
if (mOverviewProxy != null) {
- mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition,
- showImeSwitcher);
+ mOverviewProxy.disable(displayId, state1, state2, animate);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status.");
+ Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
}
} catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e);
+ Log.e(TAG_OPS, "Failed to call disable()", e);
}
+ }
+ public void onRotationProposal(int rotation, boolean isValid) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onRotationProposal(rotation, isValid);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
+ }
+ }
+
+ public void onSystemBarAttributesChanged(int displayId, int behavior) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
+ }
}
private void updateEnabledState() {
@@ -1026,11 +966,10 @@ public class OverviewProxyService extends CurrentUserTracker implements
/** Notify changes in the nav bar button alpha */
default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
default void onHomeRotationEnabled(boolean enabled) {}
+ default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
default void onSystemUiStateChanged(int sysuiStateFlags) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
- default void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
- int backDisposition, boolean showImeSwitcher) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index aa8d710e7570..85bf98c09f59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -51,10 +51,10 @@ import android.widget.TextView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.leak.RotationUtils;
@@ -69,7 +69,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
NavigationModeController.ModeChangedListener {
private final Context mContext;
- private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
@@ -82,7 +82,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
private int taskId;
@Inject
- public ScreenPinningRequest(Context context, Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+ public ScreenPinningRequest(Context context, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
mContext = context;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mAccessibilityService = (AccessibilityManager)
@@ -266,8 +266,9 @@ public class ScreenPinningRequest implements View.OnClickListener,
.setVisibility(View.INVISIBLE);
}
- NavigationBarView navigationBarView = mStatusBarOptionalLazy.map(
- statusBarLazy -> statusBarLazy.get().getNavigationBarView()).orElse(null);
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ NavigationBarView navigationBarView =
+ statusBarOptional.map(StatusBar::getNavigationBarView).orElse(null);
final boolean recentsVisible = navigationBarView != null
&& navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8e52b0da54ef..c7f8dcf87eff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -50,6 +50,7 @@ import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -337,7 +338,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
*/
default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) { }
+ @Behavior int behavior, InsetsState requestedState, String packageName) { }
/**
* @see IStatusBar#showTransient(int, int[]).
@@ -996,7 +997,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.argi1 = displayId;
@@ -1004,7 +1005,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
args.argi3 = navbarColorManagedByIme ? 1 : 0;
args.arg1 = appearanceRegions;
args.argi4 = behavior;
- args.argi5 = isFullscreen ? 1 : 0;
+ args.arg2 = requestedState;
+ args.arg3 = packageName;
mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget();
}
}
@@ -1387,7 +1389,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2,
(AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4,
- args.argi5 == 1);
+ (InsetsState) args.arg2, (String) args.arg3);
}
args.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index e7e9404c9d3e..c681a4471584 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -285,6 +285,7 @@ public class NotificationLockscreenUserManagerImpl implements
internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+ mCurrentUserId = ActivityManager.getCurrentUser(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
mSettingsObserver.onChange(false); // set up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 25cbdc5c2187..8969b4d1bcbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -84,6 +84,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import dagger.Lazy;
@@ -132,7 +133,7 @@ public class NotificationMediaManager implements Dumpable {
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
private final ArrayList<MediaListener> mMediaListeners;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -177,7 +178,7 @@ public class NotificationMediaManager implements Dumpable {
*/
public NotificationMediaManager(
Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -197,7 +198,7 @@ public class NotificationMediaManager implements Dumpable {
mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(
Context.MEDIA_SESSION_SERVICE);
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mEntryManager = notificationEntryManager;
mMainExecutor = mainExecutor;
@@ -694,7 +695,8 @@ public class NotificationMediaManager implements Dumpable {
NotificationShadeWindowController windowController =
mNotificationShadeWindowController.get();
- boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
+ boolean hideBecauseOccluded =
+ mStatusBarOptionalLazy.get().map(StatusBar::isOccluded).orElse(false);
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 4552138761c0..bc3883ca76ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -45,6 +45,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RemoteViews;
+import android.widget.RemoteViews.InteractionHandler;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +71,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
@@ -118,7 +120,7 @@ public class NotificationRemoteInputManager implements Dumpable {
private final Handler mMainHandler;
private final ActionClickLogger mLogger;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
protected final Context mContext;
private final UserManager mUserManager;
@@ -134,14 +136,14 @@ public class NotificationRemoteInputManager implements Dumpable {
protected Callback mCallback;
protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
- private final RemoteViews.InteractionHandler
- mInteractionHandler = new RemoteViews.InteractionHandler() {
+ private final InteractionHandler mInteractionHandler = new InteractionHandler() {
@Override
public boolean onInteraction(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
- mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
- "NOTIFICATION_CLICK");
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"));
final NotificationEntry entry = getNotificationForParent(view.getParent());
mLogger.logInitialClick(entry, pendingIntent);
@@ -280,7 +282,7 @@ public class NotificationRemoteInputManager implements Dumpable {
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarStateController statusBarStateController,
@Main Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -290,7 +292,7 @@ public class NotificationRemoteInputManager implements Dumpable {
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
mEntryManager = notificationEntryManager;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mMainHandler = mainHandler;
mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cd5cce4f3ee7..3bd7dd339a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -183,7 +183,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
viewState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing()
|| algorithmState.firstViewInShelf == null;
final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 0725bf961e13..d4f5bd241c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
@@ -23,10 +27,16 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.os.SystemProperties;
import android.text.format.DateFormat;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.InsetsFlags;
+import android.view.InsetsState;
import android.view.View;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
@@ -56,6 +66,9 @@ import javax.inject.Inject;
public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
CallbackController<StateListener>, Dumpable {
private static final String TAG = "SbStateController";
+ private static final boolean DEBUG_IMMERSIVE_APPS =
+ SystemProperties.getBoolean("persist.debug.immersive_apps", false);
+
// Must be a power of 2
private static final int HISTORY_SIZE = 32;
@@ -420,7 +433,10 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
@Override
- public void setFullscreenState(boolean isFullscreen) {
+ public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+ InsetsState requestedState, String packageName) {
+ boolean isFullscreen = !requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ || !requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
if (mIsFullscreen != isFullscreen) {
mIsFullscreen = isFullscreen;
synchronized (mListeners) {
@@ -429,6 +445,19 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
}
}
+
+ // TODO (b/190543382): Finish the logging logic.
+ // This section can be removed if we don't need to print it on logcat.
+ if (DEBUG_IMMERSIVE_APPS) {
+ boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
+ String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
+ String requestedVisibilityString = requestedState.toSourceVisibilityString();
+ if (requestedVisibilityString.isEmpty()) {
+ requestedVisibilityString = "none";
+ }
+ Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
+ + " requested visibilities: " + requestedVisibilityString);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 25200501a916..0bbae2aa2955 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -19,7 +19,10 @@ package com.android.systemui.statusbar;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.view.InsetsState;
import android.view.View;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -155,9 +158,10 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
boolean isKeyguardRequested();
/**
- * Set the fullscreen state
+ * Set the system bar attributes
*/
- void setFullscreenState(boolean isFullscreen);
+ void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+ InsetsState requestedState, String packageName);
/**
* Set pulsing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 491959320ab7..71f667aa2a81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -92,7 +92,7 @@ public interface StatusBarDependenciesModule {
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -103,7 +103,7 @@ public interface StatusBarDependenciesModule {
lockscreenUserManager,
smartReplyController,
notificationEntryManager,
- statusBarLazy,
+ statusBarOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
@@ -116,7 +116,7 @@ public interface StatusBarDependenciesModule {
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -129,7 +129,7 @@ public interface StatusBarDependenciesModule {
MediaDataManager mediaDataManager) {
return new NotificationMediaManager(
context,
- statusBarLazy,
+ statusBarOptionalLazy,
notificationShadeWindowController,
notificationEntryManager,
mediaArtworkProcessor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 94ee868ceebc..6369a7ecb6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -127,11 +127,8 @@ public final class NotificationEntry extends ListEntry {
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
- // Mimetype and Uri used to display the image in the notification *after* it has been sent.
public String remoteInputMimeType;
public Uri remoteInputUri;
- // ContentInfo used to keep the attachment permission alive until RemoteInput is sent or
- // cancelled.
public ContentInfo remoteInputAttachment;
private Notification.BubbleMetadata mBubbleMetadata;
private ShortcutInfo mShortcutInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 6964838e7e41..f75811478195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -126,7 +126,7 @@ public interface NotificationsModule {
@Provides
static NotificationGutsManager provideNotificationGutsManager(
Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -145,7 +145,7 @@ public interface NotificationsModule {
ShadeController shadeController) {
return new NotificationGutsManager(
context,
- statusBarLazy,
+ statusBarOptionalLazy,
mainHandler,
bgHandler,
accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 4319e29985d8..668d541e1770 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -116,7 +116,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@VisibleForTesting
protected String mKeyToRemoveOnGutsClosed;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -135,7 +135,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
* Injected constructor. See {@link NotificationsModule}.
*/
public NotificationGutsManager(Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -153,7 +153,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
OnUserInteractionCallback onUserInteractionCallback,
ShadeController shadeController) {
mContext = context;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
mAccessibilityManager = accessibilityManager;
@@ -564,13 +564,13 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
Runnable r = () -> mMainHandler.post(
() -> openGutsInternal(view, x, y, menuItem));
- mStatusBarLazy.get().executeRunnableDismissingKeyguard(
- r,
- null /* cancelAction */,
- false /* dismissShade */,
- true /* afterKeyguardGone */,
- true /* deferred */);
-
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.executeRunnableDismissingKeyguard(
+ r,
+ null /* cancelAction */,
+ false /* dismissShade */,
+ true /* afterKeyguardGone */,
+ true /* deferred */));
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 9846b28ba5f9..4559e1350f12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -72,7 +72,6 @@ public class AmbientState {
private boolean mPanelFullWidth;
private boolean mPulsing;
private boolean mUnlockHintRunning;
- private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
private float mHideAmount;
private boolean mAppearing;
@@ -503,14 +502,6 @@ public class AmbientState {
return mUnlockHintRunning;
}
- public boolean isQsCustomizerShowing() {
- return mQsCustomizerShowing;
- }
-
- public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
- mQsCustomizerShowing = qsCustomizerShowing;
- }
-
public void setIntrinsicPadding(int intrinsicPadding) {
mIntrinsicPadding = intrinsicPadding;
}
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 4ad72027b046..623e563d87fe 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
@@ -89,7 +89,6 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
@@ -112,7 +111,6 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.leak.RotationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -156,7 +154,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* gap is drawn between them). In this case we don't want to round their corners.
*/
private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
- private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
+ private boolean mKeyguardBypassEnabled;
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
@@ -213,7 +211,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private GroupMembershipManager mGroupMembershipManager;
private GroupExpansionManager mGroupExpansionManager;
- private NotificationActivityStarter mNotificationActivityStarter;
private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
@@ -413,6 +410,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mBackwardScrollable;
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
+ private float mKeyguardBottomPadding = -1;
private int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
@@ -565,6 +563,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
+ @Nullable
+ private OnClickListener mManageButtonClickListener;
+
@Inject
public NotificationStackScrollLayout(
@Named(VIEW_CONTEXT) Context context,
@@ -651,12 +652,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * Sets whether keyguard bypass is enabled. If true, this layout will be rendered in bypass
+ * mode when it is on the keyguard.
+ */
+ public void setKeyguardBypassEnabled(boolean isEnabled) {
+ mKeyguardBypassEnabled = isEnabled;
+ }
+
+ /**
* @return the height at which we will wake up when pulsing
*/
public float getWakeUpHeight() {
ExpandableView firstChild = getFirstChildWithBackground();
if (firstChild != null) {
- if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
+ if (mKeyguardBypassEnabled) {
return firstChild.getHeadsUpHeightWithoutHeader();
} else {
return firstChild.getCollapsedHeight();
@@ -737,6 +746,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugPaint.setColor(Color.YELLOW);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ y = (int) mMaxLayoutHeight;
+ mDebugPaint.setColor(Color.MAGENTA);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+ if (mKeyguardBottomPadding >= 0) {
+ y = getHeight() - (int) mKeyguardBottomPadding;
+ mDebugPaint.setColor(Color.GRAY);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ }
+
y = getHeight() - getEmptyBottomMargin();
mDebugPaint.setColor(Color.GREEN);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -784,7 +803,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
boolean shouldDrawBackground;
- if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+ if (mKeyguardBypassEnabled && onKeyguard()) {
shouldDrawBackground = isPulseExpanding();
} else {
shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild;
@@ -899,15 +918,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void reinitView() {
- initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper);
+ initView(getContext(), mSwipeHelper);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void initView(Context context,
- KeyguardBypassEnabledProvider keyguardBypassEnabledProvider,
- NotificationSwipeHelper swipeHelper) {
+ void initView(Context context, NotificationSwipeHelper swipeHelper) {
mScroller = new OverScroller(getContext());
- mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider;
mSwipeHelper = swipeHelper;
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
@@ -949,7 +965,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return;
}
// Portrait is easy, just use the dimen for paddings
- if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) {
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mSidePaddings = mMinimumPaddings;
return;
}
@@ -1347,7 +1363,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void notifyAppearChangedListeners() {
float appear;
float expandAmount;
- if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+ if (mKeyguardBypassEnabled && onKeyguard()) {
appear = calculateAppearFractionBypass();
expandAmount = getPulseHeight();
} else {
@@ -2385,8 +2401,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
minTopPosition = firstVisibleSection.getBounds().top;
}
boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
- && (mAmbientState.isDozing()
- || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
+ && (mAmbientState.isDozing() || (mKeyguardBypassEnabled && onKeyguard));
for (NotificationSection section : mSections) {
int minBottomPosition = minTopPosition;
if (section == lastSection) {
@@ -2529,7 +2544,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} else {
mTopPaddingOverflow = 0;
}
- setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled());
+ setTopPadding(topPadding, animate && !mKeyguardBypassEnabled);
setExpandedHeight(mExpandedHeight);
}
@@ -3093,7 +3108,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean performDisappearAnimation = !mIsExpanded
// Only animate if we still have pinned heads up, otherwise we just have the
// regular collapse animation of the lock screen
- || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
+ || (mKeyguardBypassEnabled && onKeyguard()
&& mInHeadsUpPinnedMode);
if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked()
@@ -4316,7 +4331,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// Since we are clipping to the outline we need to make sure that the shadows aren't
// clipped when pulsing
float ownTranslationZ = 0;
- if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
+ if (mKeyguardBypassEnabled && mAmbientState.isHiddenAtAll()) {
ExpandableView firstChildNotGone = getFirstChildNotGone();
if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) {
ownTranslationZ = firstChildNotGone.getTranslationZ();
@@ -4358,6 +4373,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return -1;
}
+ /**
+ * Returns whether or not a History button is shown in the footer. If there is no footer, then
+ * this will return false.
+ **/
+ public boolean isHistoryShown() {
+ return mFooterView != null && mFooterView.isHistoryShown();
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void setFooterView(@NonNull FooterView footerView) {
int index = -1;
@@ -4367,6 +4390,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
mFooterView = footerView;
addView(mFooterView, index);
+ if (mManageButtonClickListener != null) {
+ mFooterView.setManageButtonClickListener(mManageButtonClickListener);
+ }
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4772,6 +4798,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
+ /**
+ * This is used for debugging only; it will be used to draw the otherwise invisible line which
+ * NotificationPanelViewController treats as the bottom when calculating how many notifications
+ * appear on the keyguard.
+ * Setting a negative number will disable rendering this line.
+ */
+ public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+ mKeyguardBottomPadding = keyguardBottomPadding;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
mShouldShowShelfOnly = shouldShowShelfOnly;
@@ -4854,25 +4890,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setQsCustomizerShowing(boolean isShowing) {
- mAmbientState.setQsCustomizerShowing(isShowing);
- requestChildrenUpdate();
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ pw.println(String.format("[%s: pulsing=%s visibility=%s"
+ " alpha=%f scrollY:%d maxTopPadding=%d showShelfOnly=%s"
+ " qsExpandFraction=%f"
+ " hideAmount=%f]",
this.getClass().getSimpleName(),
mPulsing ? "T" : "f",
- mAmbientState.isQsCustomizerShowing() ? "T" : "f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
: "invisible",
@@ -5070,9 +5099,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- public void setNotificationActivityStarter(
- NotificationActivityStarter notificationActivityStarter) {
- mNotificationActivityStarter = notificationActivityStarter;
+ /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
+ public void setManageButtonClickListener(@Nullable OnClickListener listener) {
+ mManageButtonClickListener = listener;
+ if (mFooterView != null) {
+ mFooterView.setManageButtonClickListener(mManageButtonClickListener);
+ }
}
@VisibleForTesting
@@ -5086,9 +5118,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
clearNotifications(ROWS_ALL, true /* closeShade */);
});
- footerView.setManageButtonClickListener(v -> {
- mNotificationActivityStarter.startHistoryIntent(v, mFooterView.isHistoryShown());
- });
setFooterView(footerView);
}
@@ -5139,7 +5168,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
public float setPulseHeight(float height) {
mAmbientState.setPulseHeight(height);
- if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
+ if (mKeyguardBypassEnabled) {
notifyAppearChangedListeners();
}
requestChildrenUpdate();
@@ -5642,6 +5671,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSwipeHelper.resetExposedMenuView(animate, force);
}
+ boolean isUsingSplitNotificationShade() {
+ return mShouldUseSplitNotificationShade;
+ }
+
static boolean matchesSelection(
ExpandableNotificationRow row,
@SelectedRows int selection) {
@@ -6066,10 +6099,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/** Only rows where entry.isHighPriority() is false. */
public static final int ROWS_GENTLE = 2;
- interface KeyguardBypassEnabledProvider {
- boolean getBypassEnabled();
- }
-
interface DismissListener {
void onDismiss(@SelectedRows int selectedRows);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 09afedb6de59..8ee0cd1ac690 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -48,6 +48,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.jank.InteractionJankMonitor;
@@ -184,6 +186,9 @@ public class NotificationStackScrollLayoutController {
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
+ @Nullable
+ private NotificationActivityStarter mNotificationActivityStarter;
+
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
/**
@@ -708,8 +713,15 @@ public class NotificationStackScrollLayoutController {
});
}
- mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
- mSwipeHelper);
+ mView.initView(mView.getContext(), mSwipeHelper);
+ mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
+ mKeyguardBypassController
+ .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
+ mView.setManageButtonClickListener(v -> {
+ if (mNotificationActivityStarter != null) {
+ mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown());
+ }
+ });
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
@@ -1116,11 +1128,16 @@ public class NotificationStackScrollLayoutController {
/**
* Update whether we should show the empty shade view (no notifications in the shade).
* If so, send the update to our view.
+ *
+ * When in split mode, notifications are always visible regardless of the state of the
+ * QuickSettings panel. That being the case, empty view is always shown if the other conditions
+ * are true.
*/
public void updateShowEmptyShadeView() {
mShowEmptyShadeView = mBarState != KEYGUARD
- && !mView.isQsExpanded()
+ && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
&& mView.getVisibleNotificationCount() == 0;
+
mView.updateEmptyShadeView(
mShowEmptyShadeView,
mZenModeController.areNotificationsHiddenInShade());
@@ -1199,6 +1216,16 @@ public class NotificationStackScrollLayoutController {
mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
}
+ /**
+ * This is used for debugging only; it will be used to draw the otherwise invisible line which
+ * NotificationPanelViewController treats as the bottom when calculating how many notifications
+ * appear on the keyguard.
+ * Setting a negative number will disable rendering this line.
+ */
+ public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+ mView.setKeyguardBottomPadding(keyguardBottomPadding);
+ }
+
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
@@ -1465,6 +1492,10 @@ public class NotificationStackScrollLayoutController {
mView.animateNextTopPaddingChange();
}
+ public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) {
+ mNotificationActivityStarter = activityStarter;
+ }
+
/**
* Enum for UiEvent logged from this class
*/
@@ -1536,7 +1567,8 @@ public class NotificationStackScrollLayoutController {
@Override
public void setNotificationActivityStarter(
NotificationActivityStarter notificationActivityStarter) {
- mView.setNotificationActivityStarter(notificationActivityStarter);
+ NotificationStackScrollLayoutController.this
+ .setNotificationActivityStarter(notificationActivityStarter);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 1361acb1e156..a68dab9f046b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -58,9 +58,12 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -86,7 +89,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private View mCenteredIconArea;
private int mDisabled1;
private int mDisabled2;
- private StatusBar mStatusBarComponent;
+ private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private DarkIconManager mDarkIconManager;
private View mOperatorNameFrame;
private CommandQueue mCommandQueue;
@@ -118,13 +121,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
NotificationIconAreaController notificationIconAreaController,
- FeatureFlags featureFlags
+ FeatureFlags featureFlags,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy
) {
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mLocationPublisher = locationPublisher;
mNotificationIconAreaController = notificationIconAreaController;
mFeatureFlags = featureFlags;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
@Override
@@ -133,7 +138,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mNetworkController = Dependency.get(NetworkController.class);
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
- mStatusBarComponent = Dependency.get(StatusBar.class);
mCommandQueue = Dependency.get(CommandQueue.class);
}
@@ -269,7 +273,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
protected int adjustDisableFlags(int state) {
- boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
+ boolean headsUpVisible = mStatusBarOptionalLazy.get()
+ .map(StatusBar::headsUpShouldBeVisible).orElse(false);
if (headsUpVisible) {
state |= DISABLE_CLOCK;
}
@@ -297,7 +302,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
// The shelf will be hidden when dozing with a custom clock, we must show notification
// icons in this occasion.
if (mStatusBarStateController.isDozing()
- && mStatusBarComponent.getPanelController().hasCustomClock()) {
+ && mStatusBarOptionalLazy.get().map(
+ sb -> sb.getPanelController().hasCustomClock()).orElse(false)) {
state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
}
@@ -338,10 +344,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private boolean shouldHideNotificationIcons() {
- if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (!mStatusBar.isClosed()
+ && statusBarOptional.map(
+ StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
return true;
}
- if (mStatusBarComponent.hideStatusBarIconsForBouncer()) {
+ if (statusBarOptional.map(StatusBar::hideStatusBarIconsForBouncer).orElse(false)) {
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 2cb0a3a28901..99df3f18729e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -43,6 +43,11 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
@BypassOverride private val bypassOverride: Int
private var hasFaceFeature: Boolean
private var pendingUnlock: PendingUnlock? = null
+ private val listeners = mutableListOf<OnBypassStateChangedListener>()
+
+ private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
+ override fun onFaceAuthEnabledChanged() = notifyListeners()
+ }
@IntDef(
FACE_UNLOCK_BYPASS_NO_OVERRIDE,
@@ -83,7 +88,10 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
}
return enabled && mKeyguardStateController.isFaceAuthEnabled
}
- private set
+ private set(value) {
+ field = value
+ notifyListeners()
+ }
var bouncerShowing: Boolean = false
var altBouncerShowing: Boolean = false
@@ -140,6 +148,8 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
})
}
+ private fun notifyListeners() = listeners.forEach { it.onBypassStateChanged(bypassEnabled) }
+
/**
* Notify that the biometric unlock has happened.
*
@@ -223,6 +233,32 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
pw.println(" hasFaceFeature: $hasFaceFeature")
}
+ /** Registers a listener for bypass state changes. */
+ fun registerOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
+ val start = listeners.isEmpty()
+ listeners.add(listener)
+ if (start) {
+ mKeyguardStateController.addCallback(faceAuthEnabledChangedCallback)
+ }
+ }
+
+ /**
+ * Unregisters a listener for bypass state changes, previous registered with
+ * [registerOnBypassStateChangedListener]
+ */
+ fun unregisterOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
+ listeners.remove(listener)
+ if (listeners.isEmpty()) {
+ mKeyguardStateController.removeCallback(faceAuthEnabledChangedCallback)
+ }
+ }
+
+ /** Listener for bypass state change events. */
+ interface OnBypassStateChangedListener {
+ /** Invoked when bypass becomes enabled or disabled. */
+ fun onBypassStateChanged(isEnabled: Boolean)
+ }
+
companion object {
const val BYPASS_FADE_DURATION = 67
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 7d134057ee76..c213707a8953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -21,6 +21,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
+import android.view.InsetsState;
import android.view.View;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -149,7 +150,7 @@ public class LightsOutNotifController {
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
if (displayId != mDisplayId) {
return;
}
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 3c1892d4b7ea..67b89eea9989 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -21,11 +21,16 @@ import static android.view.View.GONE;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.START;
+import static androidx.constraintlayout.widget.ConstraintSet.TOP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
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 java.lang.Float.isNaN;
@@ -58,6 +63,8 @@ import android.os.SystemClock;
import android.os.UserManager;
import android.os.VibrationEffect;
import android.provider.Settings;
+import android.transition.ChangeBounds;
+import android.transition.TransitionManager;
import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -328,8 +335,11 @@ public class NotificationPanelViewController extends PanelViewController {
private final int mMaxKeyguardNotifications;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
+ private final SplitShadeHeaderController mSplitShadeHeaderController;
private final RecordingController mRecordingController;
private boolean mShouldUseSplitNotificationShade;
+ // The bottom padding reserved for elements of the keyguard measuring notifications
+ private float mKeyguardNotificationBottomPadding;
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
@@ -391,7 +401,7 @@ public class NotificationPanelViewController extends PanelViewController {
private float mDownY;
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
- private int mSplitShadeNotificationsTopPadding;
+ private int mSplitShadeStatusBarHeight;
private final KeyguardClockPositionAlgorithm
mClockPositionAlgorithm =
@@ -631,6 +641,8 @@ public class NotificationPanelViewController extends PanelViewController {
private KeyguardMediaController mKeyguardMediaController;
+ private boolean mStatusViewCentered = false;
+
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -716,6 +728,7 @@ public class NotificationPanelViewController extends PanelViewController {
RecordingController recordingController,
@Main Executor uiExecutor,
SecureSettings secureSettings,
+ SplitShadeHeaderController splitShadeHeaderController,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
NotificationRemoteInputManager remoteInputManager) {
super(view, falsingManager, dozeLog, keyguardStateController,
@@ -749,6 +762,7 @@ public class NotificationPanelViewController extends PanelViewController {
mShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
mView.setWillNotDraw(!DEBUG);
+ mSplitShadeHeaderController = splitShadeHeaderController;
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
mFalsingCollector = falsingCollector;
@@ -1018,8 +1032,8 @@ public class NotificationPanelViewController extends PanelViewController {
public void updateResources() {
mQuickQsOffsetHeight = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mSplitShadeNotificationsTopPadding =
- mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade);
+ mSplitShadeStatusBarHeight =
+ mResources.getDimensionPixelSize(R.dimen.split_shade_header_height);
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
mShouldUseSplitNotificationShade =
@@ -1028,6 +1042,11 @@ public class NotificationPanelViewController extends PanelViewController {
if (mQs != null) {
mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
}
+
+ int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
+ mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
+ mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade);
+
// To change the constraints at runtime, all children of the ConstraintLayout must have ids
ensureAllViewsHaveIds(mNotificationContainerParent);
ConstraintSet constraintSet = new ConstraintSet();
@@ -1040,15 +1059,18 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.connect(
R.id.notification_stack_scroller, START,
R.id.qs_edge_guideline, START);
- constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END);
} else {
constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
- constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END);
}
constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
+ constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
+ constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
+ mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
+
+ updateKeyguardStatusViewAlignment(false /* animate */);
mKeyguardMediaController.refreshMediaPosition();
}
@@ -1187,9 +1209,12 @@ public class NotificationPanelViewController extends PanelViewController {
if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
+ mNotificationStackScrollLayoutController.setKeyguardBottomPadding(
+ mKeyguardNotificationBottomPadding);
} else {
// no max when not on the keyguard
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
+ mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f);
}
}
@@ -1267,7 +1292,14 @@ public class NotificationPanelViewController extends PanelViewController {
updateClockAppearance();
}
if (!onKeyguard) {
- stackScrollerPadding = getUnlockedStackScrollerPadding();
+ if (mShouldUseSplitNotificationShade) {
+ // Quick settings are not on the top of the notifications
+ // when in split shade mode (they are on the left side),
+ // so we should not add a padding for them
+ stackScrollerPadding = 0;
+ } else {
+ stackScrollerPadding = getUnlockedStackScrollerPadding();
+ }
} else {
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
@@ -1288,7 +1320,12 @@ public class NotificationPanelViewController extends PanelViewController {
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+ if (hasVisibleNotifications && !mShouldUseSplitNotificationShade) {
+ mKeyguardStatusViewController.displayClock(SMALL);
+ } else {
+ mKeyguardStatusViewController.displayClock(LARGE);
+ }
+ updateKeyguardStatusViewAlignment(true /* animate */);
int userIconHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
float expandedFraction =
@@ -1332,6 +1369,26 @@ public class NotificationPanelViewController extends PanelViewController {
updateClock();
}
+ private void updateKeyguardStatusViewAlignment(boolean animate) {
+ boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+ boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications;
+ if (mStatusViewCentered != shouldBeCentered) {
+ mStatusViewCentered = shouldBeCentered;
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline;
+ constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
+ if (animate) {
+ ChangeBounds transition = new ChangeBounds();
+ transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition);
+ }
+ constraintSet.applyTo(mNotificationContainerParent);
+ }
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
@@ -1360,6 +1417,7 @@ public class NotificationPanelViewController extends PanelViewController {
float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
bottomPadding = Math.max(lockIconPadding, bottomPadding);
+ mKeyguardNotificationBottomPadding = bottomPadding;
float availableSpace =
mNotificationStackScrollLayoutController.getHeight()
@@ -2269,7 +2327,8 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQSExpansionEnabledAmbient() {
final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
- mQsExpansionEnabledAmbient = mAmbientState.getScrollY() <= scrollRangeToTop;
+ mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
+ || (mAmbientState.getScrollY() <= scrollRangeToTop);
setQsExpansionEnabled();
}
@@ -2301,8 +2360,8 @@ public class NotificationPanelViewController extends PanelViewController {
left = 0;
right = getView().getRight() + mDisplayRightInset;
} else {
- top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
- bottom = mNotificationStackScrollLayoutController.getHeight();
+ top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight);
+ bottom = top + mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
right = mNotificationStackScrollLayoutController.getRight();
}
@@ -2401,7 +2460,7 @@ public class NotificationPanelViewController extends PanelViewController {
int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
- int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+ int nsslBottom = bottom;
int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
mNotificationStackScrollLayoutController.setRoundedClippingBounds(
nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
@@ -2442,7 +2501,7 @@ public class NotificationPanelViewController extends PanelViewController {
private float calculateNotificationsTopPadding() {
if (mShouldUseSplitNotificationShade && !mKeyguardShowing) {
- return mSplitShadeNotificationsTopPadding + mQsNotificationTopPadding;
+ return 0;
}
if (mKeyguardShowing && (mQsExpandImmediate
|| mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -4424,6 +4483,8 @@ public class NotificationPanelViewController extends PanelViewController {
maybeAnimateBottomAreaAlpha();
resetHorizontalPanelPosition();
updateQsState();
+ mSplitShadeHeaderController.setShadeExpanded(
+ mBarState == SHADE || mBarState == SHADE_LOCKED);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5e105bb64350..1a404dc8d758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -474,7 +474,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
for (StatusBarWindowCallback cb : activeCallbacks) {
cb.onStateChanged(mCurrentState.mKeyguardShowing,
mCurrentState.mKeyguardOccluded,
- mCurrentState.mBouncerShowing);
+ mCurrentState.mBouncerShowing,
+ mCurrentState.mDozing);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index ed8fb31aea32..68e28cdba975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -22,7 +22,6 @@ import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
-import android.widget.FrameLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -31,7 +30,6 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
import java.util.Comparator;
@@ -42,8 +40,8 @@ import java.util.Comparator;
public class NotificationsQuickSettingsContainer extends ConstraintLayout
implements FragmentListener, AboveShelfObserver.HasViewAboveShelfChangedListener {
- private FrameLayout mQsFrame;
- private NotificationStackScrollLayout mStackScroller;
+ private View mQsFrame;
+ private View mStackScroller;
private View mKeyguardStatusBar;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
@@ -52,10 +50,10 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private int mBottomPadding;
private int mStackScrollerMargin;
- private boolean mHasViewsAboveShelf;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
+ private boolean mSplitShadeEnabled;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -144,7 +142,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
public void setCustomizerShowing(boolean isShowing) {
mCustomizing = isShowing;
updateBottomMargin();
- mStackScroller.setQsCustomizerShowing(isShowing);
}
public void setDetailShowing(boolean isShowing) {
@@ -152,8 +149,19 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
updateBottomMargin();
}
+ /**
+ * Sets if split shade is enabled and adjusts margins/paddings depending on QS details and
+ * customizer state
+ */
+ public void setSplitShadeEnabled(boolean splitShadeEnabled) {
+ mSplitShadeEnabled = splitShadeEnabled;
+ // in case device was rotated while showing QS details/customizer
+ updateBottomMargin();
+ }
+
private void updateBottomMargin() {
- if (mCustomizing || mDetailShowing) {
+ // in split shade, QS state changes should not influence notifications panel
+ if (!mSplitShadeEnabled && (mCustomizing || mDetailShowing)) {
// Clear out bottom paddings/margins so the qs customization can be full height.
setPadding(0, 0, 0, 0);
setBottomMargin(mStackScroller, 0);
@@ -171,7 +179,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
@Override
public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
- mHasViewsAboveShelf = hasViewsAboveShelf;
invalidate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cfcea9684c3b..44ed279ba187 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -359,21 +359,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mAnimateChange = state.getAnimateChange();
mAnimationDuration = state.getAnimationDuration();
- mInFrontTint = state.getFrontTint();
- mBehindTint = state.getBehindTint();
- mNotificationsTint = state.getNotifTint();
- mBubbleTint = state.getBubbleTint();
-
- mInFrontAlpha = state.getFrontAlpha();
- mBehindAlpha = state.getBehindAlpha();
- mBubbleAlpha = state.getBubbleAlpha();
- mNotificationsAlpha = state.getNotifAlpha();
- if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
- throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: "
- + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
- + mNotificationsAlpha);
- }
- applyStateToAlpha();
+ applyState();
// Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
// We need to disable focus otherwise AOD would end up with a gray overlay.
@@ -637,7 +623,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- private void applyStateToAlpha() {
+ private void applyState() {
+ mInFrontTint = mState.getFrontTint();
+ mBehindTint = mState.getBehindTint();
+ mNotificationsTint = mState.getNotifTint();
+ mBubbleTint = mState.getBubbleTint();
+
+ mInFrontAlpha = mState.getFrontAlpha();
+ mBehindAlpha = mState.getBehindAlpha();
+ mBubbleAlpha = mState.getBubbleAlpha();
+ mNotificationsAlpha = mState.getNotifAlpha();
+
+ assertAlphasValid();
+
if (!mExpansionAffectsAlpha) {
return;
}
@@ -695,6 +693,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mBehindTint = behindTint;
}
}
+
+ assertAlphasValid();
+ }
+
+ private void assertAlphasValid() {
if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
+ ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
@@ -734,7 +737,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void applyAndDispatchState() {
- applyStateToAlpha();
+ applyState();
if (mUpdatePending) {
return;
}
@@ -1203,6 +1206,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
pw.println(" ScrimController: ");
pw.print(" state: ");
pw.println(mState);
+ pw.println(" mClipQsScrim = " + mState.mClipQsScrim);
pw.print(" frontScrim:");
pw.print(" viewAlpha=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index d4458e29a306..edacbe1b9696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -48,7 +48,7 @@ public class ShadeControllerImpl implements ShadeController {
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final int mDisplayId;
- protected final Lazy<StatusBar> mStatusBarLazy;
+ protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Optional<Bubbles> mBubblesOptional;
@@ -61,7 +61,7 @@ public class ShadeControllerImpl implements ShadeController {
NotificationShadeWindowController notificationShadeWindowController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
WindowManager windowManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<AssistManager> assistManagerLazy,
Optional<Bubbles> bubblesOptional
) {
@@ -71,7 +71,7 @@ public class ShadeControllerImpl implements ShadeController {
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
// TODO: Remove circular reference to StatusBar when possible.
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mAssistManagerLazy = assistManagerLazy;
mBubblesOptional = bubblesOptional;
}
@@ -210,7 +210,7 @@ public class ShadeControllerImpl implements ShadeController {
}
private StatusBar getStatusBar() {
- return mStatusBarLazy.get();
+ return mStatusBarOptionalLazy.get().get();
}
private NotificationPresenter getPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
new file mode 100644
index 000000000000..d29107da7420
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.phone
+
+import android.view.View
+import com.android.systemui.BatteryMeterView
+import com.android.systemui.R
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
+import javax.inject.Inject
+import javax.inject.Named
+
+@StatusBarScope
+class SplitShadeHeaderController @Inject constructor(
+ @Named(SPLIT_SHADE_HEADER) private val statusBar: View,
+ private val statusBarIconController: StatusBarIconController,
+ qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
+ featureFlags: FeatureFlags
+) {
+
+ private val iconManager: StatusBarIconController.IconManager
+ private val qsCarrierGroupController: QSCarrierGroupController
+ private var visible = false
+
+ var shadeExpanded = false
+ set(value) {
+ field = value
+ updateVisibility()
+ }
+
+ var splitShadeMode = false
+ set(value) {
+ field = value
+ updateVisibility()
+ }
+
+ init {
+ val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
+ // battery settings same as in QS icons
+ batteryIcon.setIgnoreTunerUpdates(true)
+ batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+ val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+ iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
+ qsCarrierGroupController = qsCarrierGroupControllerBuilder
+ .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
+ .build()
+ }
+
+ private fun updateVisibility() {
+ val shouldBeVisible = shadeExpanded && splitShadeMode
+ if (visible != shouldBeVisible) {
+ visible = shouldBeVisible
+ statusBar.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE
+ updateListeners(shouldBeVisible)
+ }
+ }
+
+ private fun updateListeners(visible: Boolean) {
+ qsCarrierGroupController.setListening(visible)
+ if (visible) {
+ statusBarIconController.addIconGroup(iconManager)
+ } else {
+ statusBarIconController.removeIconGroup(iconManager)
+ }
+ }
+} \ No newline at end of file
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 db7ead7c1531..f78a003d0295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -105,6 +105,7 @@ import android.util.Slog;
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -988,7 +989,8 @@ public class StatusBar extends SystemUI implements DemoMode,
showTransientUnchecked();
}
onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,
- result.mNavbarColorManagedByIme, result.mBehavior, result.mAppFullscreen);
+ result.mNavbarColorManagedByIme, result.mBehavior, result.mRequestedState,
+ result.mPackageName);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
@@ -1190,7 +1192,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mAnimationScheduler,
mStatusBarLocationPublisher,
mNotificationIconAreaController,
- mFeatureFlags),
+ mFeatureFlags,
+ () -> Optional.of(this)),
CollapsedStatusBarFragment.TAG)
.commit();
@@ -1451,7 +1454,7 @@ public class StatusBar extends SystemUI implements DemoMode,
.setNotificationPresenter(mPresenter)
.setNotificationPanelViewController(mNotificationPanelViewController)
.build();
- mStackScroller.setNotificationActivityStarter(mNotificationActivityStarter);
+ mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
mNotificationsController.initialize(
@@ -2491,7 +2494,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
if (displayId != mDisplayId) {
return;
}
@@ -2504,7 +2507,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarMode, navbarColorManagedByIme);
updateBubblesVisibility();
- mStatusBarStateController.setFullscreenState(isFullscreen);
+ mStatusBarStateController.setSystemBarAttributes(
+ appearance, behavior, requestedState, packageName);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8a7708aaa8c2..95fd886d6046 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -970,6 +970,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBar.setBouncerShowing(bouncerShowing);
}
+ if (occluded != mLastOccluded || mFirstUpdate) {
+ mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded);
+ }
if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
mKeyguardUpdateManager.onKeyguardVisibilityChanged(showing && !occluded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index f33ff2732cda..ac43b679da0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -16,5 +16,6 @@
package com.android.systemui.statusbar.phone;
public interface StatusBarWindowCallback {
- void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing);
+ void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing,
+ boolean isDozing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 9a25a7078859..3d3b58ae0c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -34,6 +36,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
import android.view.IWindowManager;
+import android.view.Surface;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -118,21 +121,7 @@ public class StatusBarWindowController {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
- mLp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- mBarHeight,
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- PixelFormat.TRANSLUCENT);
- mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
- mLp.token = new Binder();
- mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(0 /* types */);
- mLp.setTitle("StatusBar");
- mLp.packageName = mContext.getPackageName();
- mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
@@ -141,6 +130,63 @@ public class StatusBarWindowController {
calculateStatusBarLocationsForAllRotations();
}
+ private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
+ WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
+ lp.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
+ }
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
+ int height = mBarHeight;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ int defaultAndUpsideDownHeight;
+ int theOtherHeight;
+ if (displayBounds.width() > displayBounds.height()) {
+ defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
+ theOtherHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ } else {
+ defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ theOtherHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
+ }
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = defaultAndUpsideDownHeight;
+ break;
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ height = theOtherHeight;
+ break;
+ }
+ }
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ height,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+ lp.token = new Binder();
+ lp.gravity = Gravity.TOP;
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.setTitle("StatusBar");
+ lp.packageName = mContext.getPackageName();
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ return lp;
+
+ }
+
private void calculateStatusBarLocationsForAllRotations() {
Rect[] bounds = new Rect[4];
bounds[0] = mContentInsetsProvider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index fb25ae37ea74..4e632c718091 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -23,6 +23,7 @@ import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import java.lang.annotation.Documented;
@@ -86,4 +87,10 @@ public interface StatusBarComponent {
*/
@StatusBarScope
AuthRippleController getAuthRippleController();
+
+ /**
+ * Creates a SplitShadeStatusBarController.
+ */
+ @StatusBarScope
+ SplitShadeHeaderController getSplitShadeStatusBarController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 27d71edd5e8a..d691dca1c898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.dagger;
import android.annotation.Nullable;
+import android.view.View;
import com.android.keyguard.LockIconView;
import com.android.systemui.R;
@@ -25,11 +26,16 @@ import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.TapAgainView;
+import javax.inject.Named;
+
import dagger.Module;
import dagger.Provides;
@Module
public abstract class StatusBarViewModule {
+
+ public static final String SPLIT_SHADE_HEADER = "split_shade_header";
+
/** */
@Provides
@StatusBarComponent.StatusBarScope
@@ -57,6 +63,15 @@ public abstract class StatusBarViewModule {
/** */
@Provides
+ @Named(SPLIT_SHADE_HEADER)
+ @StatusBarComponent.StatusBarScope
+ public static View getSlitShadeStatusBarView(
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.split_shade_status_bar);
+ }
+
+ /** */
+ @Provides
@StatusBarComponent.StatusBarScope
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index ab58286859cc..5a3d72555d76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
+import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;
@@ -157,6 +158,15 @@ public class AccessPointControllerImpl
}
@Override
+ public MergedCarrierEntry getMergedCarrierEntry() {
+ if (mWifiPickerTracker == null) {
+ fireAcccessPointsCallback(Collections.emptyList());
+ return null;
+ }
+ return mWifiPickerTracker.getMergedCarrierEntry();
+ }
+
+ @Override
public int getIcon(WifiEntry ap) {
int level = ap.getLevel();
return ICONS[Math.max(0, level)];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index fcfc9670b8b0..742b1ab69727 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -245,5 +245,11 @@ public interface KeyguardStateController extends CallbackController<Callback> {
* animation.
*/
default void onKeyguardDismissAmountChanged() {}
+
+ /**
+ * Triggered when face auth becomes available or unavailable. Value should be queried with
+ * {@link KeyguardStateController#isFaceAuthEnabled()}.
+ */
+ default void onFaceAuthEnabledChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index ef2ca985858d..6b71f46238e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -23,6 +23,7 @@ import android.telephony.SubscriptionInfo;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -223,6 +224,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
+ MergedCarrierEntry getMergedCarrierEntry();
int getIcon(WifiEntry ap);
boolean connect(WifiEntry ap);
boolean canConfigWifi();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index fa6111589bff..acbbde5fad60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -68,8 +68,11 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+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.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -189,6 +192,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
private boolean mUserSetup;
private boolean mSimDetected;
private boolean mForceCellularValidated;
+ private InternetDialogFactory mInternetDialogFactory;
+ private Handler mMainHandler;
private ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -218,6 +223,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
AccessPointControllerImpl accessPointController,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
+ @Main Handler handler,
+ InternetDialogFactory internetDialogFactory,
FeatureFlags featureFlags) {
this(context, connectivityManager,
telephonyManager,
@@ -238,6 +245,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
carrierConfigTracker,
featureFlags);
mReceiverHandler.post(mRegisterListeners);
+ mMainHandler = handler;
+ mInternetDialogFactory = internetDialogFactory;
}
@VisibleForTesting
@@ -472,6 +481,9 @@ 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);
+ }
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -780,6 +792,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
+ case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
+ mMainHandler.post(() -> mInternetDialogFactory.create(true));
+ break;
default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 84d7c05ddc14..7f3152c08572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -72,11 +72,10 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -151,7 +150,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@UiEvent(doc = "User sent data through the notification remote input view")
NOTIFICATION_REMOTE_INPUT_SEND(797),
@UiEvent(doc = "Failed attempt to send data through the notification remote input view")
- NOTIFICATION_REMOTE_INPUT_FAILURE(798);
+ NOTIFICATION_REMOTE_INPUT_FAILURE(798),
+ @UiEvent(doc = "User attached an image to the remote input view")
+ NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE(825);
private final int mId;
NotificationRemoteInputEvent(int id) {
@@ -283,7 +284,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
});
}
- private void setAttachment(ContentInfo item) {
+ @VisibleForTesting
+ protected void setAttachment(ContentInfo item) {
if (mEntry.remoteInputAttachment != null && mEntry.remoteInputAttachment != item) {
// We need to release permissions when sending the attachment to the target
// app or if it is deleted by the user. When sending to the target app, we
@@ -309,6 +311,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
attachment.setVisibility(GONE);
} else {
attachment.setVisibility(VISIBLE);
+ mUiEventLogger.logWithInstanceId(
+ NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE,
+ mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
+ mEntry.getSbn().getInstanceId());
}
updateSendButton();
}
@@ -413,8 +419,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEntry.getSbn().getPackageName(),
mEntry.getSbn().getUser().getIdentifier());
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -423,8 +427,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mPendingIntent.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.i(TAG, "Unable to send remote input result", e);
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_FAILURE,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -501,8 +503,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
if (logClose) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_CLOSE,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -584,8 +584,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
public void focus() {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4e921a036b36..107fb94ba13c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -23,6 +23,7 @@ import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -71,9 +72,11 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -104,9 +107,12 @@ public class UserSwitcherController implements Dumpable {
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
protected final Context mContext;
+ protected final UserTracker mUserTracker;
protected final UserManager mUserManager;
+ private final ContentObserver mSettingsObserver;
private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
- private final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
+ @VisibleForTesting
+ final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
private final KeyguardStateController mKeyguardStateController;
protected final Handler mHandler;
private final ActivityStarter mActivityStarter;
@@ -115,15 +121,18 @@ public class UserSwitcherController implements Dumpable {
private final IActivityTaskManager mActivityTaskManager;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
- private Dialog mExitGuestDialog;
- private Dialog mAddUserDialog;
+ @VisibleForTesting
+ AlertDialog mExitGuestDialog;
+ @VisibleForTesting
+ Dialog mAddUserDialog;
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
// When false, there won't be any visual affordance to add a new user from the keyguard even if
// the user is unlocked
private boolean mAddUsersFromLockScreen;
- private boolean mPauseRefreshUsers;
+ @VisibleForTesting
+ boolean mPauseRefreshUsers;
private int mSecondaryUser = UserHandle.USER_NULL;
private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
@@ -136,6 +145,8 @@ public class UserSwitcherController implements Dumpable {
@Inject
public UserSwitcherController(Context context,
+ UserManager userManager,
+ UserTracker userTracker,
KeyguardStateController keyguardStateController,
@Main Handler handler,
ActivityStarter activityStarter,
@@ -145,14 +156,17 @@ public class UserSwitcherController implements Dumpable {
TelephonyListenerManager telephonyListenerManager,
IActivityTaskManager activityTaskManager,
UserDetailAdapter userDetailAdapter,
+ SecureSettings secureSettings,
@UiBackground Executor uiBgExecutor) {
mContext = context;
+ mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
mFalsingManager = falsingManager;
- mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(this, mUiEventLogger);
+ mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
+ this, mUserTracker, mUiEventLogger, secureSettings);
mUserDetailAdapter = userDetailAdapter;
mUiBgExecutor = uiBgExecutor;
if (!UserManager.isGuestUserEphemeral()) {
@@ -164,7 +178,7 @@ public class UserSwitcherController implements Dumpable {
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mActivityStarter = activityStarter;
- mUserManager = UserManager.get(context);
+ mUserManager = userManager;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -183,6 +197,14 @@ public class UserSwitcherController implements Dumpable {
mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
PERMISSION_SELF, null /* scheduler */);
+ mSettingsObserver = new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
+ mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
+ refreshUsers(UserHandle.USER_NULL);
+ };
+ };
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
mSettingsObserver);
@@ -244,11 +266,11 @@ public class UserSwitcherController implements Dumpable {
return null;
}
ArrayList<UserRecord> records = new ArrayList<>(infos.size());
- int currentId = ActivityManager.getCurrentUser();
+ int currentId = mUserTracker.getUserId();
// Check user switchability of the foreground user since SystemUI is running in
// User 0
boolean canSwitchUsers = mUserManager.getUserSwitchability(
- UserHandle.of(ActivityManager.getCurrentUser())) == SWITCHABILITY_STATUS_OK;
+ UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
UserInfo currentUserInfo = null;
UserRecord guestRecord = null;
@@ -376,7 +398,7 @@ public class UserSwitcherController implements Dumpable {
}
public void logoutCurrentUser() {
- int currentUser = ActivityManager.getCurrentUser();
+ int currentUser = mUserTracker.getUserId();
if (currentUser != UserHandle.USER_SYSTEM) {
pauseRefreshUsers();
ActivityManager.logoutCurrentUser();
@@ -388,7 +410,7 @@ public class UserSwitcherController implements Dumpable {
Log.w(TAG, "User " + userId + " could not removed.");
return;
}
- if (ActivityManager.getCurrentUser() == userId) {
+ if (mUserTracker.getUserId() == userId) {
switchToUserId(UserHandle.USER_SYSTEM);
}
if (mUserManager.removeUser(userId)) {
@@ -396,7 +418,8 @@ public class UserSwitcherController implements Dumpable {
}
}
- private void onUserListItemClicked(UserRecord record) {
+ @VisibleForTesting
+ void onUserListItemClicked(UserRecord record) {
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
@@ -414,7 +437,7 @@ public class UserSwitcherController implements Dumpable {
id = record.info.id;
}
- int currUserId = ActivityManager.getCurrentUser();
+ int currUserId = mUserTracker.getUserId();
if (currUserId == id) {
if (record.isGuest) {
showExitGuestDialog(id);
@@ -565,15 +588,6 @@ public class UserSwitcherController implements Dumpable {
}
};
- private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
- public void onChange(boolean selfChange) {
- mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
- mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
- refreshUsers(UserHandle.USER_NULL);
- };
- };
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UserSwitcherController state:");
@@ -632,13 +646,7 @@ public class UserSwitcherController implements Dumpable {
* UserHandle.USER_NULL}, then switch immediately to the newly created guest user.
*/
public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
- UserInfo currentUser;
- try {
- currentUser = ActivityManager.getService().getCurrentUser();
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't remove guest because ActivityManager is dead");
- return;
- }
+ UserInfo currentUser = mUserTracker.getUserInfo();
if (currentUser.id != guestUserId) {
Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+ " is not current user (" + currentUser.id + ")");
@@ -836,9 +844,9 @@ public class UserSwitcherController implements Dumpable {
private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
- UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
+ UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId());
if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
- UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
+ UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId())) {
record.isDisabledByAdmin = true;
record.enforcedAdmin = admin;
} else {
@@ -1050,7 +1058,8 @@ public class UserSwitcherController implements Dumpable {
}
}
- private final class AddUserDialog extends SystemUIDialog implements
+ @VisibleForTesting
+ final class AddUserDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
public AddUserDialog(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 81999b534046..935524454fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -325,6 +325,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+ // All wallpaper color and keyguard logic only applies when Monet is enabled.
+ if (!mIsMonetEnabled) {
+ return;
+ }
+
// Upon boot, make sure we have the most up to date colors
Runnable updateColors = () -> {
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java
new file mode 100644
index 000000000000..542cf6559c64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java
@@ -0,0 +1,198 @@
+/*
+ * 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.util.concurrency;
+
+/**
+ * Allows triggering methods based on a passed in id or message, generally on another thread.
+ *
+ * Messages sent on to this router must be processed in order. That is to say, if three
+ * messages are sent with no delay, they must be processed in the order they were sent. Moreover,
+ * if messages are sent with various delays, they must be processed in order of their delay.
+ *
+ * Messages can be passed by either a simple integer or an instance of a class. Unique integers are
+ * considered unique messages. Unique message classes (not instances) are considered unique
+ * messages. You can use message classes to pass extra data for processing to subscribers.
+ *
+ * <pre>
+ * // Three messages with three unique integer messages.
+ * // They can be subscribed to independently.
+ * router.sendMessage(0);
+ * router.sendMessage(1);
+ * router.sendMessage(2);
+ *
+ * // Three messages with two unique message classes.
+ * // The first and third messages will be delivered to the same subscribers.
+ * router.sendMessage(new Foo(0));
+ * router.sendMessage(new Bar(1));
+ * router.sendMessage(new Foo(2));
+ * </pre>
+ *
+ * The number of unique ids and message types used should be relatively constrained. Construct
+ * a custom message-class and put unique, per-message data inside of it.
+ */
+public interface MessageRouter {
+ /**
+ * Alerts any listeners subscribed to the passed in id.
+ *
+ * The number of unique ids used should be relatively constrained - used to identify the type
+ * of message being sent. If unique information needs to be passed with each call, use
+ * {@link #sendMessage(Object)}.
+ *
+ * @param id An identifier for the message
+ */
+ default void sendMessage(int id) {
+ sendMessageDelayed(id, 0);
+ }
+
+ /**
+ * Alerts any listeners subscribed to the passed in message.
+ *
+ * The number of message types used should be relatively constrained. If no unique information
+ * needs to be passed in, you can simply use {@link #sendMessage(int)}} which takes an integer
+ * instead of a unique class type.
+ *
+ * The class of the passed in object will be used to router the message.
+ *
+ * @param data A message containing extra data for processing.
+ */
+ default void sendMessage(Object data) {
+ sendMessageDelayed(data, 0);
+ }
+
+ /**
+ * Alerts any listeners subscribed to the passed in id in the future.
+ *
+ * The number of unique ids used should be relatively constrained - used to identify the type
+ * of message being sent. If unique information needs to be passed with each call, use
+ * {@link #sendMessageDelayed(Object, long)}.
+ *
+ * @param id An identifier for the message
+ * @param delayMs Number of milliseconds to wait before alerting.
+ */
+ void sendMessageDelayed(int id, long delayMs);
+
+
+ /**
+ * Alerts any listeners subscribed to the passed in message in the future.
+ *
+ * The number of message types used should be relatively constrained. If no unique information
+ * needs to be passed in, you can simply use {@link #sendMessageDelayed(int, long)} which takes
+ * an integer instead of a unique class type.
+ *
+ * @param data A message containing extra data for processing.
+ * @param delayMs Number of milliseconds to wait before alerting.
+ */
+ void sendMessageDelayed(Object data, long delayMs);
+
+ /**
+ * Cancel all unprocessed messages for a given id.
+ *
+ * If a message has multiple listeners and one of those listeners has been alerted, the other
+ * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
+ * that are still queued.
+ *
+ * @param id The message id to cancel.
+ */
+ void cancelMessages(int id);
+
+ /**
+ * Cancel all unprocessed messages for a given message type.
+ *
+ * If a message has multiple listeners and one of those listeners has been alerted, the other
+ * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
+ * that are still queued.
+ *
+ * @param messageType The class of the message to cancel
+ */
+ <T> void cancelMessages(Class<T> messageType);
+
+ /**
+ * Add a listener for a message that does not handle any extra data.
+ *
+ * See also {@link #subscribeTo(Class, DataMessageListener)}.
+ *
+ * @param id The message id to listener for.
+ * @param listener
+ */
+ void subscribeTo(int id, SimpleMessageListener listener);
+
+ /**
+ * Add a listener for a message of a specific type.
+ *
+ * See also {@link #subscribeTo(Class, DataMessageListener)}.
+ *
+ * @param messageType The class of message to listen for.
+ * @param listener
+ */
+ <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener);
+
+ /**
+ * Remove a listener for a specific message.
+ *
+ * See also {@link #unsubscribeFrom(Class, DataMessageListener)}
+ *
+ * @param id The message id to stop listening for.
+ * @param listener The listener to remove.
+ */
+ void unsubscribeFrom(int id, SimpleMessageListener listener);
+
+ /**
+ * Remove a listener for a specific message.
+ *
+ * See also {@link #unsubscribeFrom(int, SimpleMessageListener)}.
+ *
+ * @param messageType The class of message to stop listening for.
+ * @param listener The listener to remove.
+ */
+ <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener);
+
+ /**
+ * Remove a listener for all messages that it is subscribed to.
+ *
+ * See also {@link #unsubscribeFrom(DataMessageListener)}.
+ *
+ * @param listener The listener to remove.
+ */
+ void unsubscribeFrom(SimpleMessageListener listener);
+
+ /**
+ * Remove a listener for all messages that it is subscribed to.
+ *
+ * See also {@link #unsubscribeFrom(SimpleMessageListener)}.
+ *
+ * @param listener The listener to remove.
+ */
+ <T> void unsubscribeFrom(DataMessageListener<T> listener);
+
+ /**
+ * A Listener interface for when no extra data is expected or desired.
+ */
+ interface SimpleMessageListener {
+ /** */
+ void onMessage(int id);
+ }
+
+ /**
+ * A Listener interface for when extra data is expected or desired.
+ *
+ * @param <T>
+ */
+ interface DataMessageListener<T> {
+ /** */
+ void onMessage(T data);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java
new file mode 100644
index 000000000000..7145c775fe39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java
@@ -0,0 +1,186 @@
+/*
+ * 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.util.concurrency;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of {@link MessageRouter}.
+ */
+public class MessageRouterImpl implements MessageRouter {
+
+ private final DelayableExecutor mDelayableExecutor;
+
+ private final Map<Integer, List<Runnable>> mIdMessageCancelers = new HashMap<>();
+ private final Map<Class<Object>, List<Runnable>> mDataMessageCancelers = new HashMap<>();
+ private final Map<Integer, List<SimpleMessageListener>> mSimpleMessageListenerMap =
+ new HashMap<>();
+ private final Map<Class<?>, List<DataMessageListener<Object>>> mDataMessageListenerMap =
+ new HashMap<>();
+
+ public MessageRouterImpl(DelayableExecutor delayableExecutor) {
+ mDelayableExecutor = delayableExecutor;
+ }
+
+ @Override
+ public void sendMessageDelayed(int id, long delayMs) {
+ addCanceler(id, mDelayableExecutor.executeDelayed(() -> onMessage(id), delayMs));
+ }
+
+ @Override
+ public void sendMessageDelayed(Object data, long delayMs) {
+ addCanceler((Class<Object>) data.getClass(), mDelayableExecutor.executeDelayed(
+ () -> onMessage(data), delayMs));
+ }
+
+ @Override
+ public void cancelMessages(int id) {
+ synchronized (mIdMessageCancelers) {
+ if (mIdMessageCancelers.containsKey(id)) {
+ for (Runnable canceler : mIdMessageCancelers.get(id)) {
+ canceler.run();
+ }
+ // Remove, don't clear, otherwise this could look like a memory leak as
+ // more and more unique message ids are passed in.
+ mIdMessageCancelers.remove(id);
+ }
+ }
+ }
+
+ @Override
+ public <T> void cancelMessages(Class<T> messageType) {
+ synchronized (mDataMessageCancelers) {
+ if (mDataMessageCancelers.containsKey(messageType)) {
+ for (Runnable canceler : mDataMessageCancelers.get(messageType)) {
+ canceler.run();
+ }
+ // Remove, don't clear, otherwise this could look like a memory leak as
+ // more and more unique message types are passed in.
+ mDataMessageCancelers.remove(messageType);
+ }
+ }
+ }
+
+ @Override
+ public void subscribeTo(int id, SimpleMessageListener listener) {
+ synchronized (mSimpleMessageListenerMap) {
+ mSimpleMessageListenerMap.putIfAbsent(id, new ArrayList<>());
+ mSimpleMessageListenerMap.get(id).add(listener);
+ }
+ }
+
+ @Override
+ public <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener) {
+ synchronized (mDataMessageListenerMap) {
+ mDataMessageListenerMap.putIfAbsent(messageType, new ArrayList<>());
+ mDataMessageListenerMap.get(messageType).add((DataMessageListener<Object>) listener);
+ }
+ }
+
+ @Override
+ public void unsubscribeFrom(int id, SimpleMessageListener listener) {
+ synchronized (mSimpleMessageListenerMap) {
+ if (mSimpleMessageListenerMap.containsKey(id)) {
+ mSimpleMessageListenerMap.get(id).remove(listener);
+ }
+ }
+ }
+
+ @Override
+ public <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener) {
+ synchronized (mDataMessageListenerMap) {
+ if (mDataMessageListenerMap.containsKey(messageType)) {
+ mDataMessageListenerMap.get(messageType).remove(listener);
+ }
+ }
+ }
+
+ @Override
+ public void unsubscribeFrom(SimpleMessageListener listener) {
+ synchronized (mSimpleMessageListenerMap) {
+ for (Integer id : mSimpleMessageListenerMap.keySet()) {
+ mSimpleMessageListenerMap.get(id).remove(listener);
+ }
+ }
+ }
+
+ @Override
+ public <T> void unsubscribeFrom(DataMessageListener<T> listener) {
+ synchronized (mDataMessageListenerMap) {
+ for (Class<?> messageType : mDataMessageListenerMap.keySet()) {
+ mDataMessageListenerMap.get(messageType).remove(listener);
+ }
+ }
+ }
+
+ private void addCanceler(int id, Runnable canceler) {
+ synchronized (mIdMessageCancelers) {
+ mIdMessageCancelers.putIfAbsent(id, new ArrayList<>());
+ mIdMessageCancelers.get(id).add(canceler);
+ }
+ }
+
+ private void addCanceler(Class<Object> data, Runnable canceler) {
+ synchronized (mDataMessageCancelers) {
+ mDataMessageCancelers.putIfAbsent(data, new ArrayList<>());
+ mDataMessageCancelers.get(data).add(canceler);
+ }
+ }
+
+ private void onMessage(int id) {
+ synchronized (mSimpleMessageListenerMap) {
+ if (mSimpleMessageListenerMap.containsKey(id)) {
+ for (SimpleMessageListener listener : mSimpleMessageListenerMap.get(id)) {
+ listener.onMessage(id);
+ }
+ }
+ }
+
+ synchronized (mIdMessageCancelers) {
+ if (mIdMessageCancelers.containsKey(id) && !mIdMessageCancelers.get(id).isEmpty()) {
+ mIdMessageCancelers.get(id).remove(0);
+ if (mIdMessageCancelers.get(id).isEmpty()) {
+ mIdMessageCancelers.remove(id);
+ }
+ }
+ }
+ }
+
+ private void onMessage(Object data) {
+ synchronized (mDataMessageListenerMap) {
+ if (mDataMessageListenerMap.containsKey(data.getClass())) {
+ for (DataMessageListener<Object> listener : mDataMessageListenerMap.get(
+ data.getClass())) {
+ listener.onMessage(data);
+ }
+ }
+ }
+
+ synchronized (mDataMessageCancelers) {
+ if (mDataMessageCancelers.containsKey(data.getClass())
+ && !mDataMessageCancelers.get(data.getClass()).isEmpty()) {
+ mDataMessageCancelers.get(data.getClass()).remove(0);
+ if (mDataMessageCancelers.get(data.getClass()).isEmpty()) {
+ mDataMessageCancelers.remove(data.getClass());
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index b9b20c73c5d5..e9e794ea884b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -170,4 +170,20 @@ public abstract class SysUIConcurrencyModule {
public static Executor provideUiBackgroundExecutor() {
return Executors.newSingleThreadExecutor();
}
+
+ /** */
+ @Provides
+ @Main
+ public static MessageRouter providesMainMessageRouter(
+ @Main DelayableExecutor executor) {
+ return new MessageRouterImpl(executor);
+ }
+
+ /** */
+ @Provides
+ @Background
+ public static MessageRouter providesBackgroundMessageRouter(
+ @Background DelayableExecutor executor) {
+ return new MessageRouterImpl(executor);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index edea3055a783..c7998882876a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -36,7 +36,6 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.Process;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -60,6 +59,8 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -110,18 +111,18 @@ public class GarbageMonitor implements Dumpable {
private static final int DO_GARBAGE_INSPECTION = 1000;
private static final int DO_HEAP_TRACK = 3000;
- private static final int GARBAGE_ALLOWANCE = 5;
+ static final int GARBAGE_ALLOWANCE = 5;
private static final String TAG = "GarbageMonitor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final Handler mHandler;
+ private final MessageRouter mMessageRouter;
private final TrackedGarbage mTrackedGarbage;
private final LeakReporter mLeakReporter;
private final Context mContext;
- private final ActivityManager mAm;
+ private final DelayableExecutor mDelayableExecutor;
private MemoryTile mQSTile;
- private DumpTruck mDumpTruck;
+ private final DumpTruck mDumpTruck;
private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
private final ArrayList<Long> mPids = new ArrayList<>();
@@ -133,13 +134,16 @@ public class GarbageMonitor implements Dumpable {
@Inject
public GarbageMonitor(
Context context,
- @Background Looper bgLooper,
+ @Background DelayableExecutor delayableExecutor,
+ @Background MessageRouter messageRouter,
LeakDetector leakDetector,
LeakReporter leakReporter) {
mContext = context.getApplicationContext();
- mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- mHandler = new BackgroundHeapCheckHandler(bgLooper);
+ mDelayableExecutor = delayableExecutor;
+ mMessageRouter = messageRouter;
+ mMessageRouter.subscribeTo(DO_GARBAGE_INSPECTION, this::doGarbageInspection);
+ mMessageRouter.subscribeTo(DO_HEAP_TRACK, this::doHeapTrack);
mTrackedGarbage = leakDetector.getTrackedGarbage();
mLeakReporter = leakReporter;
@@ -158,13 +162,13 @@ public class GarbageMonitor implements Dumpable {
return;
}
- mHandler.sendEmptyMessage(DO_GARBAGE_INSPECTION);
+ mMessageRouter.sendMessage(DO_GARBAGE_INSPECTION);
}
public void startHeapTracking() {
startTrackingProcess(
android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
- mHandler.sendEmptyMessage(DO_HEAP_TRACK);
+ mMessageRouter.sendMessage(DO_HEAP_TRACK);
}
private boolean gcAndCheckGarbage() {
@@ -586,33 +590,18 @@ public class GarbageMonitor implements Dumpable {
}
}
- private class BackgroundHeapCheckHandler extends Handler {
- BackgroundHeapCheckHandler(Looper onLooper) {
- super(onLooper);
- if (Looper.getMainLooper().equals(onLooper)) {
- throw new RuntimeException(
- "BackgroundHeapCheckHandler may not run on the ui thread");
- }
+ private void doGarbageInspection(int id) {
+ if (gcAndCheckGarbage()) {
+ mDelayableExecutor.executeDelayed(this::reinspectGarbageAfterGc, 100);
}
- @Override
- public void handleMessage(Message m) {
- switch (m.what) {
- case DO_GARBAGE_INSPECTION:
- if (gcAndCheckGarbage()) {
- postDelayed(GarbageMonitor.this::reinspectGarbageAfterGc, 100);
- }
-
- removeMessages(DO_GARBAGE_INSPECTION);
- sendEmptyMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
- break;
+ mMessageRouter.cancelMessages(DO_GARBAGE_INSPECTION);
+ mMessageRouter.sendMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
+ }
- case DO_HEAP_TRACK:
- update();
- removeMessages(DO_HEAP_TRACK);
- sendEmptyMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
- break;
- }
- }
+ private void doHeapTrack(int id) {
+ update();
+ mMessageRouter.cancelMessages(DO_HEAP_TRACK);
+ mMessageRouter.sendMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 3320852ca1f4..f31762819d00 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -104,6 +104,7 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.view.RotationPolicy;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
@@ -400,7 +401,9 @@ public class VolumeDialogImpl implements VolumeDialog,
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
+ if (!shouldSlideInVolumeTray()) {
+ mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
+ }
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
@@ -587,6 +590,10 @@ public class VolumeDialogImpl implements VolumeDialog,
return (int) (alpha * 255);
}
+ private boolean shouldSlideInVolumeTray() {
+ return mContext.getDisplay().getRotation() != RotationPolicy.NATURAL_ROTATION;
+ }
+
private boolean isLandscape() {
return mContext.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
@@ -1320,7 +1327,7 @@ public class VolumeDialogImpl implements VolumeDialog,
hideRingerDrawer();
}, 50));
- if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f);
+ if (!shouldSlideInVolumeTray()) animator.translationX(mDialogView.getWidth() / 2.0f);
animator.start();
checkODICaptionsTooltip(true);
mController.notifyVisible(false);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 2216a915b0d9..3be1d3cab869 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
@@ -144,10 +145,17 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
TvPipMenuController tvPipMenuController,
SyncTransactionQueue syncTransactionQueue,
PipBoundsState pipBoundsState,
+ PipTransitionState pipTransitionState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
@@ -157,7 +165,7 @@ public abstract class TvPipModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index bc956dc85702..f2db4f1cd901 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -64,6 +64,7 @@ import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -104,7 +105,8 @@ public final class WMShell extends SystemUI
// Shell interfaces
private final Optional<Pip> mPipOptional;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
@@ -120,6 +122,7 @@ public final class WMShell extends SystemUI
private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
+ private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@@ -128,7 +131,8 @@ public final class WMShell extends SystemUI
@Inject
public WMShell(Context context,
Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
@@ -149,6 +153,7 @@ public final class WMShell extends SystemUI
mScreenLifecycle = screenLifecycle;
mSysUiState = sysUiState;
mPipOptional = pipOptional;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
@@ -165,6 +170,7 @@ public final class WMShell extends SystemUI
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
+ mLegacySplitScreenOptional.ifPresent(this::initLegacySplitScreen);
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
@@ -218,8 +224,8 @@ public final class WMShell extends SystemUI
}
@VisibleForTesting
- void initSplitScreen(LegacySplitScreen legacySplitScreen) {
- mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ void initLegacySplitScreen(LegacySplitScreen legacySplitScreen) {
+ mLegacySplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
// Hide the divider when keyguard is showing. Even though keyguard/statusbar is
@@ -229,6 +235,17 @@ public final class WMShell extends SystemUI
legacySplitScreen.onKeyguardVisibilityChanged(showing);
}
};
+ mKeyguardUpdateMonitor.registerCallback(mLegacySplitScreenKeyguardCallback);
+ }
+
+ @VisibleForTesting
+ void initSplitScreen(SplitScreen splitScreen) {
+ mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ splitScreen.onKeyguardOccludedChanged(occluded);
+ }
+ };
mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 6ef74505a681..7e733a9721ef 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -194,11 +194,12 @@ public abstract class WMShellBaseModule {
ShellTaskOrganizer organizer,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
+ @ShellMainThread Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler));
+ uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
}
//
@@ -424,8 +425,9 @@ public abstract class WMShellBaseModule {
@Provides
static TaskViewFactoryController provideTaskViewFactoryController(
ShellTaskOrganizer shellTaskOrganizer,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor,
+ SyncTransactionQueue syncQueue) {
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
}
//
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 36fd9bee80ab..be7813e19b11 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -48,6 +48,7 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -184,8 +185,15 @@ public class WMShellModule {
@WMSingleton
@Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
SyncTransactionQueue syncTransactionQueue,
+ PipTransitionState pipTransitionState,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipMenuController menuPhoneController,
@@ -197,7 +205,7 @@ public class WMShellModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
@@ -215,8 +223,9 @@ public class WMShellModule {
static PipTransitionController providePipTransitionController(Context context,
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) {
- return new PipTransition(context, pipBoundsState, pipMenuController,
+ PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
+ PhonePipMenuController pipMenuController) {
+ return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 10ed1d75a533..ce02b8339422 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -19,6 +19,9 @@ package com.android.keyguard;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -247,4 +250,36 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
verify(plugin).setStyle(style);
}
+
+ @Test
+ public void switchingToBigClock_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE);
+
+ mKeyguardClockSwitch.mClockInAnim.end();
+ mKeyguardClockSwitch.mClockOutAnim.end();
+
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToSmallClock_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL);
+
+ mKeyguardClockSwitch.mClockInAnim.end();
+ mKeyguardClockSwitch.mClockOutAnim.end();
+
+ assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ // only big clock is removed at switch
+ assertThat(mLargeClockFrame.getParent()).isNull();
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index ca857c5dd05b..64bdc2e9dc5d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -19,11 +19,13 @@ package com.android.keyguard;
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
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.times;
import static org.mockito.Mockito.verify;
@@ -33,6 +35,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.MotionEvent;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
@@ -43,6 +46,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -58,6 +62,7 @@ import org.mockito.junit.MockitoRule;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+ private static final int VIEW_WIDTH = 1600;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -100,6 +105,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
private EmergencyButtonController mEmergencyButtonController;
@Mock
private Resources mResources;
+ @Mock
+ private FalsingCollector mFalsingCollector;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -112,7 +119,9 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED.
when(mResources.getConfiguration()).thenReturn(mConfiguration);
+ when(mView.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
+ when(mView.getWidth()).thenReturn(VIEW_WIDTH);
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
@@ -131,7 +140,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController)
+ mConfigurationController, mFalsingCollector)
.create(mSecurityCallback);
}
@@ -169,18 +178,156 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(0)).updateLayoutForSecurityMode(any());
+ verify(mView, times(0)).setOneHandedMode(anyBoolean());
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(1)).updateLayoutForSecurityMode(any());
+ verify(mView, times(1)).setOneHandedMode(anyBoolean());
}
@Test
- public void updateKeyguardPosition_callsThroughToView() {
+ public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(true);
+ mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f);
+ verify(mView).setOneHandedModeLeftAligned(true, false);
+
+ mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2);
+ verify(mView).setOneHandedModeLeftAligned(false, false);
+ }
+
+ @Test
+ public void updateKeyguardPosition_ignoredInTwoHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(false);
mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
- verify(mView).updateKeyguardPosition(1.0f);
+ verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean());
+ }
+
+ private void touchDownLeftSide() {
+ mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+ MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */VIEW_WIDTH / 3f,
+ /* y= */0,
+ /* metaState= */0));
+ }
+
+ private void touchDownRightSide() {
+ mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+ MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */(VIEW_WIDTH / 3f) * 2,
+ /* y= */0,
+ /* metaState= */0));
+ }
+
+ @Test
+ public void onInterceptTap_inhibitsFalsingInOneHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(true);
+ when(mView.isOneHandedModeLeftAligned()).thenReturn(true);
+
+ touchDownLeftSide();
+ verify(mFalsingCollector, never()).avoidGesture();
+
+ // Now on the right.
+ touchDownRightSide();
+ verify(mFalsingCollector).avoidGesture();
+
+ // Move and re-test
+ reset(mFalsingCollector);
+ when(mView.isOneHandedModeLeftAligned()).thenReturn(false);
+
+ // On the right...
+ touchDownRightSide();
+ verify(mFalsingCollector, never()).avoidGesture();
+
+ touchDownLeftSide();
+ verify(mFalsingCollector).avoidGesture();
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(true);
+ }
+
+ @Test
+ public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ private void setUpKeyguardFlags(
+ boolean deviceConfigCanUseOneHandedKeyguard,
+ boolean sysuiResourceCanUseOneHandedKeyguard) {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning))
+ .thenReturn(deviceConfigCanUseOneHandedKeyguard);
+ when(mResources.getBoolean(
+ R.bool.can_use_one_handed_bouncer))
+ .thenReturn(sysuiResourceCanUseOneHandedKeyguard);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index f5916e748f04..02763235a8ca 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableResources;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -37,8 +36,6 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -57,11 +54,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
private static final int FAKE_MEASURE_SPEC =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY);
- private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN;
- private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password;
-
-
-
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -90,45 +82,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
- public void onMeasure_usesFullWidthWithoutOneHandedMode() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesFullWidthWithDeviceFlagDisabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesFullWidthWithSysUIFlagDisabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesHalfWidthWithFlagsEnabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- ONE_HANDED_SECURITY_MODE);
+ public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */true);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -138,11 +93,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
- public void onMeasure_usesFullWidthForFullScreenIme() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- TWO_HANDED_SECURITY_MODE);
+ public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -153,10 +105,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
int imeInsetAmount = 100;
int systemBarInsetAmount = 10;
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -180,10 +129,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
int imeInsetAmount = 0;
int systemBarInsetAmount = 10;
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -201,56 +147,41 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec);
}
- private void setupForUpdateKeyguardPosition(SecurityMode securityMode) {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- securityMode);
+ private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
+ mKeyguardSecurityContainer.setOneHandedMode(oneHandedMode);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(true, false);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
- // Start off left-aligned. This should happen anyway, but just do this to ensure
- // definitely move to the left.
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
-
// Clear any interactions with the mock so we know the interactions definitely come from the
// below testing.
reset(mSecurityViewFlipper);
}
@Test
- public void updateKeyguardPosition_movesKeyguard() {
- setupForUpdateKeyguardPosition(ONE_HANDED_SECURITY_MODE);
+ public void setIsLeftAligned_movesKeyguard() {
+ setupForUpdateKeyguardPosition(/* oneHandedMode= */ true);
- mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f);
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */true, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(0.0f);
}
@Test
- public void updateKeyguardPosition_doesntMoveTwoHandedKeyguard() {
- setupForUpdateKeyguardPosition(TWO_HANDED_SECURITY_MODE);
+ public void setIsLeftAligned_doesntMoveTwoHandedKeyguard() {
+ setupForUpdateKeyguardPosition(/* oneHandedMode= */ false);
- mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */true, /* animate= */false);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
-
- private void setUpKeyguard(
- boolean deviceConfigCanUseOneHandedKeyguard,
- boolean sysuiResourceCanUseOneHandedKeyguard,
- SecurityMode securityMode) {
- TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.addOverride(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning,
- deviceConfigCanUseOneHandedKeyguard);
- testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
- sysuiResourceCanUseOneHandedKeyguard);
- mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 5617f1b6316b..fe5633eb9df8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -70,7 +70,6 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -90,7 +89,6 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
-@FlakyTest(bugId = 188890599)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MagnificationModeSwitchTest extends SysuiTestCase {
@@ -507,6 +505,30 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
}
+ @Test
+ public void onScreenSizeChanged_buttonIsShowingOnTheRightSide_expectedPosition() {
+ final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds);
+ final float windowHeightFraction =
+ (float) (mWindowManager.getLayoutParamsFromAttachedView().y
+ - oldDraggableBounds.top) / oldDraggableBounds.height();
+
+ // The window bounds and the draggable bounds are changed due to the screen size change.
+ final Rect tmpRect = new Rect(windowBounds);
+ tmpRect.scale(2);
+ final Rect newWindowBounds = new Rect(tmpRect);
+ mWindowManager.setWindowBounds(newWindowBounds);
+ mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+
+ final int expectedX = mMagnificationModeSwitch.mDraggableWindowBounds.right;
+ final int expectedY = (int) (windowHeightFraction
+ * mMagnificationModeSwitch.mDraggableWindowBounds.height())
+ + mMagnificationModeSwitch.mDraggableWindowBounds.top;
+ assertEquals(expectedX, mWindowManager.getLayoutParamsFromAttachedView().x);
+ assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
+ }
+
private void assertModeUnchanged(int expectedMode) {
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
index 9621bedd9210..8bb9d423fa92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
@@ -16,9 +16,6 @@
package com.android.systemui.accessibility;
-import static android.view.WindowInsets.Type.systemGestures;
-
-import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.view.Display;
@@ -79,14 +76,11 @@ public class TestableWindowManager implements WindowManager {
@Override
public WindowMetrics getCurrentWindowMetrics() {
- final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10);
- final WindowInsets insets = new WindowInsets.Builder()
- .setInsets(systemGestures(), systemGesturesInsets)
- .build();
+ final WindowMetrics realMetrics = mWindowManager.getCurrentWindowMetrics();
final WindowMetrics windowMetrics = new WindowMetrics(
- mWindowBounds == null ? mWindowManager.getCurrentWindowMetrics().getBounds()
+ mWindowBounds == null ? realMetrics.getBounds()
: mWindowBounds,
- mWindowInsets == null ? insets : mWindowInsets);
+ mWindowInsets == null ? realMetrics.getWindowInsets() : mWindowInsets);
return windowMetrics;
}
@@ -106,10 +100,20 @@ public class TestableWindowManager implements WindowManager {
return (WindowManager.LayoutParams) mView.getLayoutParams();
}
+ /**
+ * Sets the given window bounds to current window metrics.
+ *
+ * @param bounds the window bounds
+ */
public void setWindowBounds(Rect bounds) {
mWindowBounds = bounds;
}
+ /**
+ * Sets the given window insets to the current window metics.
+ *
+ * @param insets the window insets.
+ */
public void setWindowInsets(WindowInsets insets) {
mWindowInsets = insets;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f62069d35d35..ca6533075acb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import static android.view.Choreographer.FrameCallback;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
@@ -45,6 +46,8 @@ import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
@@ -55,11 +58,11 @@ import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -140,7 +143,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
- @FlakyTest(bugId = 188889181)
public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -239,11 +241,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
});
}
@Test
- public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() {
+ public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
final Display display = Mockito.spy(mContext.getDisplay());
when(display.getRotation()).thenReturn(Surface.ROTATION_90);
when(mContext.getDisplay()).thenReturn(display);
@@ -251,13 +255,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
});
+ final PointF expectedCenter = new PointF(mWindowMagnificationController.getCenterY(),
+ mWindowMagnificationController.getCenterX());
+ final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+ // Rotate the window 90 degrees.
+ windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+ windowBounds.right);
+ mWindowManager.setWindowBounds(windowBounds);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
});
assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
- // The first invocation is called when the surface is created.
+ final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
+ mWindowMagnificationController.getCenterY());
+ assertEquals(expectedCenter, actualCenter);
verify(mWindowManager, times(2)).updateViewLayout(any(), any());
}
@@ -275,6 +288,33 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
+ public void onScreenSizeChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
+ // The default position is at the center of the screen.
+ final float expectedRatio = 0.5f;
+ final Rect testWindowBounds = new Rect(
+ mWindowManager.getCurrentWindowMetrics().getBounds());
+ testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+ testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+ mWindowManager.setWindowBounds(testWindowBounds);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+ });
+
+ // The ratio of center to window size should be the same.
+ assertEquals(expectedRatio,
+ mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
+ 0);
+ assertEquals(expectedRatio,
+ mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
+ 0);
+ }
+
+ @Test
public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -419,9 +459,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
- public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() {
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+ public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
+ final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ final WindowInsets testInsets = new WindowInsets.Builder()
+ .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
+ .build();
+ mWindowManager.setWindowInsets(testInsets);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 397341b197ee..cdb5212b5a47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -191,7 +191,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mWindowManager,
mStatusBarStateController,
mFgExecutor,
- mStatusBar,
+ Optional.of(mStatusBar),
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index f62587c6e87c..57c57ec3273d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -49,6 +49,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -103,7 +104,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mController = new UdfpsKeyguardViewController(
mView,
mStatusBarStateController,
- mStatusBar,
+ Optional.of(mStatusBar),
mStatusBarKeyguardViewManager,
mKeyguardUpdateMonitor,
mExecutor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 578c2d976cce..8c0d21e911da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -74,6 +74,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
@SmallTest
@@ -104,7 +105,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@Mock private UiEventLogger mUiEventLogger;
- @Mock private GlobalActionsInfoProvider mInfoProvider;
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private SysUiState mSysUiState;
@@ -151,12 +151,11 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mWindowManager,
mBackgroundExecutor,
mUiEventLogger,
- mInfoProvider,
mRingerModeTracker,
mSysUiState,
mHandler,
mPackageManager,
- mStatusBar
+ Optional.of(mStatusBar)
);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 2fa67cc0be60..6d1db378bf2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -81,6 +81,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.regex.Pattern;
@@ -169,7 +170,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mSysUiState,
mHandler,
mPackageManager,
- mStatusBar
+ Optional.of(mStatusBar)
);
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
deleted file mode 100644
index 302a8d3f2efa..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
+++ /dev/null
@@ -1,134 +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.google.android.systemui.globalactions
-
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.res.Configuration
-import android.content.res.Resources
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.globalactions.GlobalActionsInfoProvider
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.SysuiTestCase
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyObject
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class GlobalActionsInfoProviderTest : SysuiTestCase() {
-
- @Mock private lateinit var walletClient: QuickAccessWalletClient
- @Mock private lateinit var controlsController: ControlsController
- @Mock private lateinit var activityStarter: ActivityStarter
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var mockResources: Resources
- @Mock private lateinit var sharedPrefs: SharedPreferences
- @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
-
- private lateinit var infoProvider: GlobalActionsInfoProvider
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- mockContext = spy(context)
- mockResources = spy(context.resources)
- whenever(mockContext.resources).thenReturn(mockResources)
- whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
- .thenReturn(true)
- whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt()))
- .thenReturn(sharedPrefs)
- whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
- whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor)
- whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean()))
- .thenReturn(sharedPrefsEditor)
-
- infoProvider = GlobalActionsInfoProvider(
- mockContext,
- walletClient,
- controlsController,
- activityStarter
- )
- }
-
- @Test
- fun testIsEligible_noCards() {
- whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
- whenever(walletClient.isWalletFeatureAvailable).thenReturn(false)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testIsEligible_hasCards() {
- whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
- whenever(walletClient.isWalletFeatureAvailable).thenReturn(true)
-
- assertTrue(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testNotEligible_shouldNotShow() {
- whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
- .thenReturn(false)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testTooManyButtons_doesNotAdd() {
- val configuration = Configuration()
- configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
- whenever(mockResources.configuration).thenReturn(configuration)
-
- val parent = mock(ViewGroup::class.java)
- infoProvider.addPanel(mockContext, parent, 5, { })
-
- verify(parent, never()).addView(anyObject(), anyInt())
- }
-
- @Test
- fun testLimitTimesShown() {
- whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index d2527c679a13..8fd2a329a33e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -81,6 +81,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
+ private CommandQueue mCommandQueue = mock(CommandQueue.class);
+
private static final int SECONDARY_DISPLAY = 1;
@Before
@@ -99,11 +101,11 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mock(StatusBarStateController.class),
mock(SysUiState.class),
mock(BroadcastDispatcher.class),
- mock(CommandQueue.class),
+ mCommandQueue,
Optional.of(mock(Pip.class)),
Optional.of(mock(LegacySplitScreen.class)),
Optional.of(mock(Recents.class)),
- () -> mock(StatusBar.class),
+ () -> Optional.of(mock(StatusBar.class)),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
@@ -112,6 +114,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
mock(ConfigurationController.class),
+ mock(NavigationBarA11yHelper.class),
+ mock(TaskbarDelegate.class),
mock(UserTracker.class)));
initializeNavigationBars();
}
@@ -275,4 +279,9 @@ public class NavigationBarControllerTest extends SysuiTestCase {
verify(mSecondaryNavBar).disableAnimationsDuringHide(eq(500L));
}
+
+ @Test
+ public void test3ButtonTaskbarFlagDisabledNoRegister() {
+ verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index eac68f6e76f4..a6ff2e8d2e15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -31,9 +31,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-import com.android.systemui.navigationbar.RotationButton;
-import com.android.systemui.navigationbar.RotationButtonController;
import com.android.systemui.statusbar.policy.RotationLockController;
import org.junit.Before;
@@ -46,7 +43,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NavigationBarRotationContextTest extends SysuiTestCase {
- static final int RES_UNDEF = 0;
static final int DEFAULT_ROTATE = 0;
@Rule
@@ -63,10 +59,18 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
final View view = new View(mContext);
mRotationButton = mock(RotationButton.class);
mRotationButtonController = new RotationButtonController(mContext, 0, 0);
- mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {});
+ mRotationButtonController.setRotationButton(mRotationButton,
+ new RotationButton.RotationButtonUpdatesCallback() {
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ }
+
+ @Override
+ public void onPositionChanged() {
+ }
+ });
// Due to a mockito issue, only spy the object after setting the initial state
mRotationButtonController = spy(mRotationButtonController);
- final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index a570675b442e..92cd244e2e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -272,7 +272,7 @@ public class NavigationBarTest extends SysuiTestCase {
Optional.of(mock(Pip.class)),
Optional.of(mock(LegacySplitScreen.class)),
Optional.of(mock(Recents.class)),
- () -> mock(StatusBar.class),
+ () -> Optional.of(mock(StatusBar.class)),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
@@ -280,6 +280,7 @@ public class NavigationBarTest extends SysuiTestCase {
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
+ mock(NavigationBarA11yHelper.class),
mock(UserTracker.class)));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
new file mode 100644
index 000000000000..0a2000107053
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
@@ -0,0 +1,127 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.view.Gravity
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@SmallTest
+internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase)
+ : SysuiTestCase() {
+
+ private val calculator = FloatingRotationButtonPositionCalculator(
+ MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM
+ )
+
+ @Test
+ fun calculatePosition() {
+ val position = calculator.calculatePosition(
+ testCase.rotation,
+ testCase.taskbarVisible,
+ testCase.taskbarStashed
+ )
+
+ assertThat(position).isEqualTo(testCase.expectedPosition)
+ }
+
+ internal class TestCase(
+ val rotation: Int,
+ val taskbarVisible: Boolean,
+ val taskbarStashed: Boolean,
+ val expectedPosition: Position
+ ) {
+ override fun toString(): String =
+ "when rotation = $rotation, " +
+ "taskbarVisible = $taskbarVisible, " +
+ "taskbarStashed = $taskbarStashed - " +
+ "expected $expectedPosition"
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<TestCase> =
+ listOf(
+ TestCase(
+ rotation = Surface.ROTATION_0,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ translationX = MARGIN_DEFAULT,
+ translationY = -MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_90,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.RIGHT,
+ translationX = -MARGIN_DEFAULT,
+ translationY = -MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_180,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.TOP or Gravity.RIGHT,
+ translationX = -MARGIN_DEFAULT,
+ translationY = MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_270,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.TOP or Gravity.LEFT,
+ translationX = MARGIN_DEFAULT,
+ translationY = MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_0,
+ taskbarVisible = true,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ translationX = MARGIN_TASKBAR_LEFT,
+ translationY = -MARGIN_TASKBAR_BOTTOM
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_0,
+ taskbarVisible = true,
+ taskbarStashed = true,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ translationX = MARGIN_DEFAULT,
+ translationY = -MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_90,
+ taskbarVisible = true,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.RIGHT,
+ translationX = -MARGIN_TASKBAR_LEFT,
+ translationY = -MARGIN_TASKBAR_BOTTOM
+ )
+ )
+ )
+
+ private const val MARGIN_DEFAULT = 10
+ private const val MARGIN_TASKBAR_LEFT = 20
+ private const val MARGIN_TASKBAR_BOTTOM = 30
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 35620329467b..e73e5ff49dd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -58,6 +58,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.time.Duration;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import dagger.Lazy;
@@ -89,7 +90,7 @@ public class PowerUITest extends SysuiTestCase {
private IThermalEventListener mSkinThermalEventListener;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
- @Mock private Lazy<StatusBar> mStatusBarLazy;
+ @Mock private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
@Mock private StatusBar mStatusBar;
@Before
@@ -98,7 +99,7 @@ public class PowerUITest extends SysuiTestCase {
mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
- when(mStatusBarLazy.get()).thenReturn(mStatusBar);
+ when(mStatusBarOptionalLazy.get()).thenReturn(Optional.of(mStatusBar));
mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
@@ -688,7 +689,8 @@ public class PowerUITest extends SysuiTestCase {
}
private void createPowerUi() {
- mPowerUI = new PowerUI(mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarLazy);
+ mPowerUI = new PowerUI(
+ mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarOptionalLazy);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index d44a52607707..e939411e4a2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -327,4 +328,77 @@ public class CastTileTest extends SysuiTestCase {
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
}
+
+ @Test
+ public void testExpandView_wifiNotConnected() {
+ mCastTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_wifiEnabledNotCasting() {
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_casting_projection() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastController.CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_connecting_projection() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_casting_mediaRoute() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ device.tag = mock(MediaRouter.RouteInfo.class);
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_connecting_mediaRoute() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.tag = mock(RouteInfo.class);
+ connecting.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
new file mode 100644
index 000000000000..a8f6f5361d78
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -0,0 +1,102 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class InternetAdapterTest extends SysuiTestCase {
+
+ private static final String WIFI_TITLE = "Wi-Fi Title";
+ private static final String WIFI_SUMMARY = "Wi-Fi Summary";
+ private InternetDialogController mInternetDialogController = mock(
+ InternetDialogController.class);
+ private InternetAdapter mInternetAdapter;
+ private InternetAdapter.InternetViewHolder mViewHolder;
+ @Mock
+ private WifiEntry mWifiEntry = mock(WifiEntry.class);
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mInternetAdapter = new InternetAdapter(mInternetDialogController);
+ mViewHolder = (InternetAdapter.InternetViewHolder) mInternetAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+ when(mInternetDialogController.getWifiEntryList()).thenReturn(Arrays.asList(mWifiEntry));
+ }
+
+ @Test
+ public void getItemCount_withApmOnWifiOnNoConnectedWifi_returnFour() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+ assertThat(mInternetAdapter.getItemCount()).isEqualTo(4);
+ }
+
+ @Test
+ public void getItemCount_withApmOnWifiOnHasConnectedWifi_returnThree() {
+ when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+ assertThat(mInternetAdapter.getItemCount()).isEqualTo(3);
+ }
+
+ @Test
+ public void getItemCount_withApmOffWifiOnNoConnectedWifi_returnThree() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+ assertThat(mInternetAdapter.getItemCount()).isEqualTo(3);
+ }
+
+ @Test
+ public void getItemCount_withApmOffWifiOnHasConnectedWifi_returnTwo() {
+ when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+ assertThat(mInternetAdapter.getItemCount()).isEqualTo(2);
+ }
+
+ @Test
+ public void onBindViewHolder_bindWithOpenWifiNetwork_verifyView() {
+ when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE);
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_bindWithSecurityWifiNetwork_verifyView() {
+ when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK);
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
new file mode 100644
index 000000000000..83e4d00b3a8f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -0,0 +1,269 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.WifiEntry;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogControllerTest extends SysuiTestCase {
+
+ private static final int SUB_ID = 1;
+ private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
+ private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
+
+ private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private MockInternetDialogController mInternetDialogController;
+ private InternetDialogController.InternetDialogCallback mCallback =
+ mock(InternetDialogController.InternetDialogCallback.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private WifiManager mWifiManager = mock(WifiManager.class);
+ private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
+ private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+ private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private GlobalSettings mGlobalSettings;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private NetworkController.AccessPointController mAccessPointController;
+ @Mock
+ private WifiEntry mWifiEntryConnected = mock(WifiEntry.class);
+ @Mock
+ private WifiInfo mWifiInfo;
+ @Mock
+ private ServiceState mServiceState;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
+ when(mWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
+ mInternetDialogController = new MockInternetDialogController(mContext, mUiEventLogger,
+ mStarter, mAccessPointController, mSubscriptionManager, mTelephonyManager,
+ mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
+ mKeyguardUpdateMonitor, mGlobalSettings);
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+ mInternetDialogController.mOnSubscriptionsChangedListener);
+ mInternetDialogController.onStart(mCallback);
+ }
+
+ @Test
+ public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
+ mInternetDialogController.setAirplaneModeEnabled(true);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+ getResourcesString("airplane_mode")));
+ }
+
+ @Test
+ public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+ getResourcesString("quick_settings_internet_label")));
+ }
+
+ @Test
+ public void getSubtitleText_withAirplaneModeOn_returnNull() {
+ mInternetDialogController.setAirplaneModeEnabled(true);
+
+ assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+ }
+
+ @Test
+ public void getSubtitleText_withWifiOff_returnWifiIsOff() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ getResourcesString("wifi_is_off")));
+ }
+
+ @Test
+ public void getSubtitleText_withWifiOn_returnSearchWifi() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(true),
+ getResourcesString("wifi_empty_list_wifi_on")));
+ }
+
+ @Test
+ public void getSubtitleText_withWifiEntry_returnTapToConnect() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ List<ScanResult> wifiScanResults = mock(ArrayList.class);
+ doReturn(1).when(wifiScanResults).size();
+ when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ getResourcesString("tap_a_network_to_connect")));
+ }
+
+ @Test
+ public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ List<ScanResult> wifiScanResults = new ArrayList<>();
+ doReturn(wifiScanResults).when(mWifiManager).getScanResults();
+ when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
+ when(mSubscriptionManager.getActiveSubscriptionIdList())
+ .thenReturn(new int[] {SUB_ID});
+
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+ doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ getResourcesString("all_network_unavailable")));
+ }
+
+ @Test
+ public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ List<ScanResult> wifiScanResults = new ArrayList<>();
+ doReturn(wifiScanResults).when(mWifiManager).getScanResults();
+ when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
+ when(mSubscriptionManager.getActiveSubscriptionIdList())
+ .thenReturn(new int[] {SUB_ID});
+
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+ doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+
+ when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ getResourcesString("non_carrier_network_unavailable")));
+ }
+
+ @Test
+ public void getConnectedWifiTitle_withNoConnectedEntry_returnNull() {
+ mInternetDialogController.setConnectedWifiEntry(null);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
+ ""));
+ }
+
+ @Test
+ public void getConnectedWifiTitle_withConnectedEntry_returnTitle() {
+ mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected);
+ when(mWifiEntryConnected.getTitle()).thenReturn(CONNECTED_TITLE);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
+ CONNECTED_TITLE));
+ }
+
+ @Test
+ public void getConnectedWifiSummary_withNoConnectedEntry_returnNull() {
+ mInternetDialogController.setConnectedWifiEntry(null);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
+ ""));
+ }
+
+ @Test
+ public void getConnectedWifiSummary_withConnectedEntry_returnSummary() {
+ mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected);
+ when(mWifiEntryConnected.getSummary(false)).thenReturn(CONNECTED_SUMMARY);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
+ CONNECTED_SUMMARY));
+ }
+
+ private String getResourcesString(String name) {
+ return mContext.getResources().getString(getResourcesId(name));
+ }
+
+ private int getResourcesId(String name) {
+ return mContext.getResources().getIdentifier(name, "string",
+ mContext.getPackageName());
+ }
+
+ private class MockInternetDialogController extends InternetDialogController {
+
+ private WifiEntry mConnectedEntry;
+ private GlobalSettings mGlobalSettings;
+ private boolean mIsAirplaneModeOn;
+
+ MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
+ ActivityStarter starter, AccessPointController accessPointController,
+ SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+ @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+ @Main Handler handler, @Main Executor mainExecutor,
+ BroadcastDispatcher broadcastDispatcher,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings) {
+ super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
+ telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
+ broadcastDispatcher, keyguardUpdateMonitor, globalSettings);
+ mGlobalSettings = globalSettings;
+ }
+
+ @Override
+ boolean isAirplaneModeEnabled() {
+ return mIsAirplaneModeOn;
+ }
+
+ public void setAirplaneModeEnabled(boolean enabled) {
+ mIsAirplaneModeOn = enabled;
+ }
+
+ @Override
+ WifiEntry getConnectedWifiEntry() {
+ return mConnectedEntry;
+ }
+
+ public void setConnectedWifiEntry(WifiEntry connectedEntry) {
+ mConnectedEntry = connectedEntry;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
new file mode 100644
index 000000000000..d394b0c9bfe2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -0,0 +1,246 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogTest extends SysuiTestCase {
+
+ private static final int SUB_ID = 1;
+ private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
+ private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
+ private static final String WIFI_TITLE = "Connected Wi-Fi Title";
+ private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
+
+ private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+
+ private InternetDialogFactory mInternetDialogFactory = mock(InternetDialogFactory.class);
+ private InternetAdapter mInternetAdapter = mock(InternetAdapter.class);
+ private InternetDialogController mInternetDialogController = mock(
+ InternetDialogController.class);
+ private InternetDialogController.InternetDialogCallback mCallback =
+ mock(InternetDialogController.InternetDialogCallback.class);
+ private MockInternetDialog mInternetDialog;
+ private WifiReceiver mWifiReceiver = null;
+ private WifiManager mMockWifiManager = mock(WifiManager.class);
+ private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+ @Mock
+ private WifiEntry mWifiEntry = mock(WifiEntry.class);
+ @Mock
+ private WifiInfo mWifiInfo;
+ @Mock
+ private Handler mHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mInternetDialog = new MockInternetDialog(mContext, mInternetDialogFactory,
+ mInternetDialogController, true, mUiEventLogger, mHandler);
+ mInternetDialog.show();
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ when(mMockWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
+ mInternetDialog.setMobileNetworkTitle(MOBILE_NETWORK_TITLE);
+ mInternetDialog.setMobileNetworkSummary(MOBILE_NETWORK_SUMMARY);
+ mInternetDialog.setConnectedWifiTitle(WIFI_TITLE);
+ mInternetDialog.setConnectedWifiSummary(WIFI_SUMMARY);
+ mWifiReceiver = new WifiReceiver();
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(mWifiReceiver, mIntentFilter);
+ when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+ when(mInternetDialogController.getWifiEntryList()).thenReturn(Arrays.asList(mWifiEntry));
+ }
+
+ @After
+ public void tearDown() {
+ mInternetDialog.dismissDialog();
+ }
+
+ @Test
+ public void updateDialog_withApmOn_internetDialogSubTitleGone() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialog.updateDialog();
+ final TextView view = mInternetDialog.mDialogView.requireViewById(
+ R.id.internet_dialog_subtitle);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ mInternetDialog.updateDialog();
+ final TextView view = mInternetDialog.mDialogView.requireViewById(
+ R.id.internet_dialog_subtitle);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_withApmOn_mobileDataLayoutGone() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialog.updateDialog();
+ final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
+ R.id.mobile_network_layout);
+
+ assertThat(linearLayout.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_withWifiOnAndHasConnectedWifi_connectedWifiLayoutVisible() {
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+ when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+ when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
+ mInternetDialog.updateDialog();
+ final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
+ R.id.wifi_connected_layout);
+
+ assertThat(linearLayout.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_withWifiOnAndNoConnectedWifi_connectedWifiLayoutGone() {
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+ mInternetDialog.updateDialog();
+ final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
+ R.id.wifi_connected_layout);
+
+ assertThat(linearLayout.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_withWifiOff_WifiRecycleViewGone() {
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false);
+ mInternetDialog.updateDialog();
+ final RecyclerView view = mInternetDialog.mDialogView.requireViewById(
+ R.id.wifi_list_layout);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onClickSeeMoreButton_clickSeeMore_verifyLaunchNetworkSetting() {
+ final LinearLayout seeAllLayout = mInternetDialog.mDialogView.requireViewById(
+ R.id.see_all_layout);
+ seeAllLayout.performClick();
+
+ verify(mInternetDialogController).launchNetworkSetting();
+ }
+
+ private class MockInternetDialog extends InternetDialog {
+
+ private String mMobileNetworkTitle;
+ private String mMobileNetworkSummary;
+ private String mConnectedWifiTitle;
+ private String mConnectedWifiSummary;
+
+ MockInternetDialog(Context context, InternetDialogFactory internetDialogFactory,
+ InternetDialogController internetDialogController, boolean aboveStatusBar,
+ UiEventLogger uiEventLogger, @Main Handler handler) {
+ super(context, internetDialogFactory, internetDialogController, aboveStatusBar,
+ uiEventLogger, handler);
+ mAdapter = mInternetAdapter;
+ mWifiManager = mMockWifiManager;
+ }
+
+ @Override
+ String getMobileNetworkTitle() {
+ return mMobileNetworkTitle;
+ }
+
+ @Override
+ String getMobileNetworkSummary() {
+ return mMobileNetworkSummary;
+ }
+
+ void setMobileNetworkTitle(String title) {
+ mMobileNetworkTitle = title;
+ }
+
+ void setMobileNetworkSummary(String summary) {
+ mMobileNetworkSummary = summary;
+ }
+
+ @Override
+ String getConnectedWifiTitle() {
+ return mConnectedWifiTitle;
+ }
+
+ @Override
+ String getConnectedWifiSummary() {
+ return mConnectedWifiSummary;
+ }
+
+ void setConnectedWifiTitle(String title) {
+ mConnectedWifiTitle = title;
+ }
+
+ void setConnectedWifiSummary(String summary) {
+ mConnectedWifiSummary = summary;
+ }
+
+ @Override
+ public void onWifiStateReceived(Context context, Intent intent) {
+ setMobileNetworkTitle(MOBILE_NETWORK_TITLE);
+ setMobileNetworkSummary(MOBILE_NETWORK_SUMMARY);
+ }
+ }
+
+ private class WifiReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ return;
+ }
+
+ if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mInternetDialog.updateDialog();
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 21c6292c151f..d5a2919880ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
+import android.view.InsetsState;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -124,24 +125,25 @@ public class CommandQueueTest extends SysuiTestCase {
public void testOnSystemBarAttributesChanged() {
doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, false);
+ BEHAVIOR_DEFAULT, new InsetsState(), "test");
}
@Test
public void testOnSystemBarAttributesChangedForSecondaryDisplay() {
doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, false);
+ BEHAVIOR_DEFAULT, new InsetsState(), "test");
}
private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedState, packageName);
waitForIdleSync();
verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance),
- eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior), eq(isFullscreen));
+ eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior),
+ eq(requestedState), eq(packageName));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 8e949e7d1e37..8b7c76a9727a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -44,6 +44,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
import dagger.Lazy;
@SmallTest
@@ -80,7 +82,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext,
mLockscreenUserManager, mSmartReplyController, mEntryManager,
- () -> mock(StatusBar.class),
+ () -> Optional.of(mock(StatusBar.class)),
mStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
@@ -265,7 +267,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -276,7 +278,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
lockscreenUserManager,
smartReplyController,
notificationEntryManager,
- statusBarLazy,
+ statusBarOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 7cbc4e4e2c62..659d96e92f34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -51,6 +51,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -90,7 +92,7 @@ public class SmartReplyControllerTest extends SysuiTestCase {
mRemoteInputManager = new NotificationRemoteInputManager(mContext,
mock(NotificationLockscreenUserManager.class), mSmartReplyController,
- mNotificationEntryManager, () -> mock(StatusBar.class),
+ mNotificationEntryManager, () -> Optional.of(mock(StatusBar.class)),
mStatusBarStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9f537f5b6afc..fc44669c5642 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -91,7 +91,6 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
@@ -99,8 +98,6 @@ import org.mockito.junit.MockitoRule;
import java.util.Optional;
-import javax.inject.Provider;
-
/**
* Tests for {@link NotificationGutsManager}.
*/
@@ -157,11 +154,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext,
- () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider,
- mINotificationManager, mNotificationEntryManager, mPeopleSpaceWidgetManager,
- mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker,
- mAssistantFeedbackController, Optional.of(mBubblesManager),
- new UiEventLoggerFake(), mOnUserInteractionCallback, mShadeController);
+ () -> Optional.of(mStatusBar), mHandler, mHandler, mAccessibilityManager,
+ mHighPriorityProvider, mINotificationManager, mNotificationEntryManager,
+ mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
+ mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
+ Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
+ mShadeController);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f376e88b2cb1..6ee2f2026deb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -92,7 +92,7 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class NotificationStackScrollerControllerTest extends SysuiTestCase {
+public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@@ -232,16 +232,15 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- true /* visible */,
-
- true /* notifVisibleInShade */);
+ /* visible= */ true,
+ /* notifVisibleInShade= */ true);
setupShowEmptyShadeViewState(stateListener, false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- false /* visible */,
- true /* notifVisibleInShade */);
+ /* visible= */ false,
+ /* notifVisibleInShade= */ true);
}
@Test
@@ -257,15 +256,42 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- true /* visible */,
- false /* notifVisibleInShade */);
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
setupShowEmptyShadeViewState(stateListener, false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- false /* visible */,
- false /* notifVisibleInShade */);
+ /* visible= */ false,
+ /* notifVisibleInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mSysuiStatusBarStateController).addCallback(
+ mStateListenerArgumentCaptor.capture(), anyInt());
+ StatusBarStateController.StateListener stateListener =
+ mStateListenerArgumentCaptor.getValue();
+ when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true);
+ stateListener.onStateChanged(SHADE);
+ mController.getView().removeAllViews();
+
+ mController.setQsExpanded(false);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
+
+ mController.setQsExpanded(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
}
@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 b03df880f0ba..3f35063fea6d 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
@@ -63,7 +63,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -101,7 +100,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
- @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@Mock private KeyguardBypassController mBypassController;
@Mock private NotificationSectionsManager mNotificationSectionsManager;
@Mock private NotificationSection mNotificationSection;
@@ -150,8 +148,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mAmbientState,
mFeatureFlags,
mUnlockedScreenOffAnimationController);
- mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
- mNotificationSwipeHelper);
+ mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper);
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelfController(notificationShelfController);
mStackScroller.setStatusBar(mBar);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 9640423425f5..32b08be51cdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -47,11 +47,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import java.util.Optional;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
+ private StatusBar mStatusBar;
private NotificationIconAreaController mMockNotificationAreaController;
private View mNotificationAreaInner;
private StatusBarStateController mStatusBarStateController;
@@ -65,12 +68,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setup() {
- StatusBar statusBar = mock(StatusBar.class);
- mDependency.injectTestDependency(StatusBar.class, statusBar);
+ mStatusBar = mock(StatusBar.class);
mStatusBarStateController = mDependency
.injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- when(statusBar.getPanelController()).thenReturn(
+ when(mStatusBar.getPanelController()).thenReturn(
mock(NotificationPanelViewController.class));
}
@@ -231,7 +233,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mAnimationScheduler,
mLocationPublisher,
mMockNotificationAreaController,
- mock(FeatureFlags.class));
+ mock(FeatureFlags.class),
+ () -> Optional.of(mStatusBar));
}
private void setUpNotificationIconAreaController() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index cdfab1eec609..c3adee95df20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -102,7 +102,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
assertTrue(mLightsOutNotifController.areLightsOut());
}
@@ -114,7 +115,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
assertFalse(mLightsOutNotifController.areLightsOut());
}
@@ -144,7 +146,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
// THEN we should show dot
assertTrue(mLightsOutNotifController.shouldShowDot());
@@ -163,7 +166,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
@@ -182,7 +186,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index f8fc22495d8d..a1e2cfcdfd97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
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;
@@ -35,6 +37,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -281,6 +284,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
+ private SplitShadeHeaderController mSplitShadeHeaderController;
+ @Mock
private ContentResolver mContentResolver;
@Mock
private TapAgainViewController mTapAgainViewController;
@@ -351,6 +356,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
mNotificationContainerParent.addView(mKeyguardStatusView);
+ mNotificationContainerParent.onFinishInflate();
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
@@ -439,6 +445,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mRecordingController,
new FakeExecutor(new FakeSystemClock()),
mSecureSettings,
+ mSplitShadeHeaderController,
mUnlockedScreenOffAnimationController,
mNotificationRemoteInputManager);
mNotificationPanelViewController.initDependencies(
@@ -577,17 +584,19 @@ public class NotificationPanelViewTest extends SysuiTestCase {
}
@Test
- public void testKeyguardStatusView_isAlignedToGuidelineInSplitShadeMode() {
- mNotificationPanelViewController.updateResources();
+ public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade();
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mNotificationPanelViewController.updateResources();
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(ConstraintSet.PARENT_ID);
+ .isEqualTo(R.id.qs_edge_guideline);
- enableSplitShade();
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mNotificationPanelViewController.updateResources();
-
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(R.id.qs_edge_guideline);
+ .isEqualTo(ConstraintSet.PARENT_ID);
}
@Test
@@ -748,6 +757,38 @@ public class NotificationPanelViewTest extends SysuiTestCase {
verify(mTapAgainViewController).show();
}
+ @Test
+ public void testSwitchesToCorrectClockInSinglePaneShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ mNotificationPanelViewController.closeQs();
+ verify(mKeyguardStatusViewController).displayClock(SMALL);
+ }
+
+ @Test
+ public void testSwitchesToCorrectClockInSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade();
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
+ verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+ }
+
+ private void triggerPositionClockAndNotifications() {
+ mNotificationPanelViewController.closeQs();
+ }
+
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 678b193073c2..2685b76d2650 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -576,6 +576,49 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
+ public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+ mScrimController.setClipsQsScrim(true);
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+
+ mScrimController.setClipsQsScrim(false);
+
+ finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible without tint
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, false,
+ mNotificationsScrim, false
+ ));
+ }
+
+ @Test
+ public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+ mScrimController.setClipsQsScrim(false);
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+
+ mScrimController.setClipsQsScrim(true);
+
+ finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be clipping QS
+ // Notif scrim should be visible without tint
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, OPAQUE,
+ mScrimBehind, OPAQUE));
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, true,
+ mNotificationsScrim, false
+ ));
+ }
+
+ @Test
public void transitionToBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index f126ed0c7555..c504fd8c7801 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -353,7 +353,7 @@ public class StatusBarTest extends SysuiTestCase {
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> mStatusBar, () -> mAssistManager, Optional.of(mBubbles));
+ () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles));
mStatusBar = new StatusBar(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index dd8354dedafd..0d4d8894c894 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.policy;
+import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -25,15 +27,19 @@ import static org.mockito.Mockito.spy;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.ContentInfo;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -238,4 +244,39 @@ public class RemoteInputViewTest extends SysuiTestCase {
RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND.getId(),
mUiEventLoggerFake.eventId(1));
}
+
+ @Test
+ public void testUiEventLogging_openAndAttach() throws Exception {
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
+ RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+
+ setTestPendingIntent(view);
+
+ // Open view, attach an image
+ view.focus();
+ EditText editText = view.findViewById(R.id.remote_input_text);
+ editText.setText(TEST_REPLY);
+ ClipDescription description = new ClipDescription("", new String[] {"image/png"});
+ // We need to use an (arbitrary) real resource here so that an actual image gets attached.
+ ClipData clip = new ClipData(description, new ClipData.Item(
+ Uri.parse("android.resource://com.android.systemui/"
+ + R.drawable.default_thumbnail)));
+ ContentInfo payload =
+ new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
+ view.setAttachment(payload);
+ mReceiver.waitForIntent();
+
+ assertEquals(2, mUiEventLoggerFake.numLogs());
+ assertEquals(
+ RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN.getId(),
+ mUiEventLoggerFake.eventId(0));
+ assertEquals(
+ RemoteInputView.NotificationRemoteInputEvent
+ .NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE.getId(),
+ mUiEventLoggerFake.eventId(1));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
new file mode 100644
index 000000000000..c365ef2002ac
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -0,0 +1,257 @@
+/*
+ * 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.policy
+
+import android.app.IActivityTaskManager
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.hardware.face.FaceManager
+import android.hardware.fingerprint.FingerprintManager
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.UserIcons
+import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.QSUserSwitcherEvent
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.telephony.TelephonyListenerManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class UserSwitcherControllerTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var handler: Handler
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var activityTaskManager: IActivityTaskManager
+ @Mock private lateinit var userDetailAdapter: UserSwitcherController.UserDetailAdapter
+ @Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
+ @Mock private lateinit var secureSettings: SecureSettings
+ @Mock private lateinit var falsingManager: FalsingManager
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var uiBgExecutor: FakeExecutor
+ private lateinit var uiEventLogger: UiEventLoggerFake
+ private lateinit var userSwitcherController: UserSwitcherController
+ private lateinit var picture: Bitmap
+ private val ownerId = UserHandle.USER_SYSTEM
+ private val ownerInfo = UserInfo(ownerId, "Owner", null,
+ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL or UserInfo.FLAG_INITIALIZED or
+ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM,
+ UserManager.USER_TYPE_FULL_SYSTEM)
+ private val guestId = 1234
+ private val guestInfo = UserInfo(guestId, "Guest", null,
+ UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ uiBgExecutor = FakeExecutor(FakeSystemClock())
+ uiEventLogger = UiEventLoggerFake()
+
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_guestUserAutoCreated, false)
+
+ mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
+ mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
+ mock(FingerprintManager::class.java))
+
+ `when`(userManager.canAddMoreUsers()).thenReturn(true)
+
+ userSwitcherController = UserSwitcherController(context,
+ userManager,
+ userTracker,
+ keyguardStateController,
+ handler,
+ activityStarter,
+ broadcastDispatcher,
+ uiEventLogger,
+ falsingManager,
+ telephonyListenerManager,
+ activityTaskManager,
+ userDetailAdapter,
+ secureSettings,
+ uiBgExecutor)
+ userSwitcherController.mPauseRefreshUsers = true
+
+ picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
+ }
+
+ @Test
+ fun testAddGuest_okButtonPressed_isLogged() {
+ val emptyGuestUserRecord = UserSwitcherController.UserRecord(
+ null,
+ null,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(ownerId)
+ `when`(userTracker.userInfo).thenReturn(ownerInfo)
+
+ `when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
+
+ userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
+ }
+
+ @Test
+ fun testRemoveGuest_removeButtonPressed_isLogged() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ true /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(guestInfo.id)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ assertNotNull(userSwitcherController.mExitGuestDialog)
+ userSwitcherController.mExitGuestDialog
+ .getButton(DialogInterface.BUTTON_POSITIVE).performClick()
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
+ }
+
+ @Test
+ fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ true /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(guestId)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ assertNotNull(userSwitcherController.mExitGuestDialog)
+ userSwitcherController.mExitGuestDialog
+ .getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
+ testableLooper.processAllMessages()
+ assertEquals(0, uiEventLogger.numLogs())
+ }
+
+ @Test
+ fun testWipeGuest_startOverButtonPressed_isLogged() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(guestId)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ // Simulate that guest user has already logged in
+ `when`(secureSettings.getIntForUser(
+ eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
+ .thenReturn(1)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+
+ // Simulate a user switch event
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
+ userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
+ userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
+ .getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_WIPE).performClick()
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_WIPE.id, uiEventLogger.eventId(0))
+ }
+
+ @Test
+ fun testWipeGuest_continueButtonPressed_isLogged() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(guestId)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ // Simulate that guest user has already logged in
+ `when`(secureSettings.getIntForUser(
+ eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
+ .thenReturn(1)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+
+ // Simulate a user switch event
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
+ userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
+ userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
+ .getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_DONTWIPE)
+ .performClick()
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE.id, uiEventLogger.eventId(0))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
new file mode 100644
index 000000000000..78fc6803ea7e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
@@ -0,0 +1,371 @@
+/*
+ * 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.util.concurrency;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MessageRouterImplTest extends SysuiTestCase {
+ private static final int MESSAGE_A = 0;
+ private static final int MESSAGE_B = 1;
+ private static final int MESSAGE_C = 2;
+
+ private static final String METADATA_A = "A";
+ private static final String METADATA_B = "B";
+ private static final String METADATA_C = "C";
+ private static final Foobar METADATA_FOO = new Foobar();
+
+ FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ @Mock
+ MessageRouter.SimpleMessageListener mNoMdListener;
+ @Mock
+ MessageRouter.DataMessageListener<String> mStringListener;
+ @Mock
+ MessageRouter.DataMessageListener<Foobar> mFoobarListener;
+ private MessageRouterImpl mMR;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMR = new MessageRouterImpl(mFakeExecutor);
+ }
+
+ @Test
+ public void testSingleMessage_NoMetaData() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ }
+
+ @Test
+ public void testSingleMessage_WithMetaData() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ }
+
+ @Test
+ public void testMessages_WithMixedMetaData() {
+ mMR.subscribeTo(String.class, mStringListener);
+ mMR.subscribeTo(Foobar.class, mFoobarListener);
+
+ mMR.sendMessage(METADATA_A);
+ verify(mStringListener, never()).onMessage(anyString());
+ verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+ reset(mStringListener);
+ reset(mFoobarListener);
+
+ mMR.sendMessage(METADATA_FOO);
+ verify(mStringListener, never()).onMessage(anyString());
+ verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+ verify(mFoobarListener).onMessage(METADATA_FOO);
+ }
+
+ @Test
+ public void testMessages_WithAndWithoutMetaData() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ reset(mNoMdListener);
+ reset(mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+ verify(mStringListener).onMessage(METADATA_A);
+ }
+
+ @Test
+ public void testRepeatedMessage() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_B);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ InOrder ordered = inOrder(mStringListener);
+ mFakeExecutor.runNextReady();
+ ordered.verify(mStringListener).onMessage(METADATA_A);
+ mFakeExecutor.runNextReady();
+ ordered.verify(mStringListener).onMessage(METADATA_A);
+ mFakeExecutor.runNextReady();
+ ordered.verify(mStringListener).onMessage(METADATA_B);
+ }
+
+ @Test
+ public void testCancelMessage() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_C, mNoMdListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_C);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mMR.cancelMessages(MESSAGE_B);
+
+ mFakeExecutor.runAllReady();
+
+ InOrder ordered = inOrder(mNoMdListener);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_A);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_C);
+ }
+
+ @Test
+ public void testSendMessage_NoSubscriber() {
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ }
+
+ @Test
+ public void testUnsubscribe_SpecificMessage_NoMetadata() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+
+ mFakeExecutor.runAllReady();
+ InOrder ordered = inOrder(mNoMdListener);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_A);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_B);
+
+ reset(mNoMdListener);
+ mMR.unsubscribeFrom(MESSAGE_A, mNoMdListener);
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(MESSAGE_A);
+ verify(mNoMdListener).onMessage(MESSAGE_B);
+ }
+
+ @Test
+ public void testUnsubscribe_SpecificMessage_WithMetadata() {
+ mMR.subscribeTo(String.class, mStringListener);
+ mMR.subscribeTo(Foobar.class, mFoobarListener);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_FOO);
+
+ mFakeExecutor.runNextReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ mFakeExecutor.runNextReady();
+ verify(mFoobarListener).onMessage(METADATA_FOO);
+
+ reset(mStringListener);
+ reset(mFoobarListener);
+ mMR.unsubscribeFrom(String.class, mStringListener);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_FOO);
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(METADATA_A);
+ verify(mFoobarListener).onMessage(METADATA_FOO);
+ }
+
+ @Test
+ public void testUnsubscribe_AllMessages_NoMetadata() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_C, mNoMdListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ verify(mNoMdListener).onMessage(MESSAGE_B);
+ verify(mNoMdListener).onMessage(MESSAGE_C);
+
+ reset(mNoMdListener);
+
+ mMR.unsubscribeFrom(mNoMdListener);
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(MESSAGE_B);
+ verify(mNoMdListener, never()).onMessage(MESSAGE_C);
+ }
+
+ @Test
+ public void testUnsubscribe_AllMessages_WithMetadata() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_B);
+ mMR.sendMessage(METADATA_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ verify(mStringListener).onMessage(METADATA_B);
+ verify(mStringListener).onMessage(METADATA_C);
+
+ reset(mStringListener);
+
+ mMR.unsubscribeFrom(mStringListener);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_B);
+ mMR.sendMessage(METADATA_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(METADATA_A);
+ verify(mStringListener, never()).onMessage(METADATA_B);
+ verify(mStringListener, never()).onMessage(METADATA_C);
+ }
+
+ @Test
+ public void testSingleDelayedMessage_NoMetaData() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+ mMR.sendMessageDelayed(MESSAGE_A, 100);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ }
+
+ @Test
+ public void testSingleDelayedMessage_WithMetaData() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessageDelayed(METADATA_C, 1000);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_C);
+ }
+
+ @Test
+ public void testMultipleDelayedMessages() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessageDelayed(METADATA_A, 100);
+ mMR.sendMessageDelayed(METADATA_B, 1000);
+ mMR.sendMessageDelayed(METADATA_C, 500);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
+
+ InOrder ordered = inOrder(mStringListener);
+ ordered.verify(mStringListener).onMessage(METADATA_A);
+ ordered.verify(mStringListener).onMessage(METADATA_C);
+ ordered.verify(mStringListener).onMessage(METADATA_B);
+ }
+
+ @Test
+ public void testCancelDelayedMessages() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessageDelayed(METADATA_A, 100);
+ mMR.sendMessageDelayed(METADATA_B, 1000);
+ mMR.sendMessageDelayed(METADATA_C, 500);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mMR.cancelMessages(String.class);
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
+
+ verify(mStringListener, never()).onMessage(anyString());
+ }
+
+ private static class Foobar {}
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
index bcc20c2d1b37..724d14e20374 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
@@ -17,124 +17,94 @@
package com.android.systemui.util.leak;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.MessageRouterImpl;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
public class GarbageMonitorTest extends SysuiTestCase {
- private LeakReporter mLeakReporter;
- private TrackedGarbage mTrackedGarbage;
- private TestableGarbageMonitor mGarbageMonitor;
+ @Mock private LeakReporter mLeakReporter;
+ @Mock private TrackedGarbage mTrackedGarbage;
+ private GarbageMonitor mGarbageMonitor;
+ private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
- mTrackedGarbage = mock(TrackedGarbage.class);
- mLeakReporter = mock(LeakReporter.class);
+ MockitoAnnotations.initMocks(this);
mGarbageMonitor =
- new TestableGarbageMonitor(
+ new GarbageMonitor(
mContext,
- TestableLooper.get(this).getLooper(),
+ mFakeExecutor,
+ new MessageRouterImpl(mFakeExecutor),
new LeakDetector(null, mTrackedGarbage, null),
mLeakReporter);
}
@Test
- public void testCallbacks_getScheduled() {
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- }
-
- @Test
- public void testNoGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
-
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
-
- verify(mLeakReporter, never()).dumpLeak(anyInt());
- }
-
- @Test
public void testALittleGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(4);
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE);
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
+ mGarbageMonitor.reinspectGarbageAfterGc();
verify(mLeakReporter, never()).dumpLeak(anyInt());
}
@Test
public void testTransientGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(100);
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
+ // Start the leak monitor. Nothing gets reported immediately.
mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runInspectCallback();
+ mFakeExecutor.runAllReady();
+ verify(mLeakReporter, never()).dumpLeak(anyInt());
+ // Garbage gets reset to 0 before the leak reporte actually gets called.
when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
- mGarbageMonitor.runReinspectCallback();
-
+ // Therefore nothing gets dumped.
verify(mLeakReporter, never()).dumpLeak(anyInt());
}
@Test
public void testLotsOfPersistentGarbage_dumps() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(100);
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
+ mGarbageMonitor.reinspectGarbageAfterGc();
- verify(mLeakReporter).dumpLeak(anyInt());
+ verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
}
- private static class TestableGarbageMonitor extends GarbageMonitor {
- public TestableGarbageMonitor(
- Context context,
- Looper looper,
- LeakDetector leakDetector,
- LeakReporter leakReporter) {
- super(context, looper, leakDetector, leakReporter);
- }
-
- void runInspectCallback() {
- startLeakMonitor();
- }
-
- void runReinspectCallback() {
- reinspectGarbageAfterGc();
- }
-
- void runCallbacksOnce() {
- // Note that TestableLooper doesn't currently support delayed messages so we need to run
- // callbacks explicitly.
- runInspectCallback();
- runReinspectCallback();
- }
+ @Test
+ public void testLotsOfPersistentGarbage_dumpsAfterAtime() {
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
+
+ // Start the leak monitor. Nothing gets reported immediately.
+ mGarbageMonitor.startLeakMonitor();
+ mFakeExecutor.runAllReady();
+ verify(mLeakReporter, never()).dumpLeak(anyInt());
+
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
+
+ verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index f243077afc93..d6492c610a4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -120,6 +120,7 @@ import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.google.common.collect.ImmutableList;
@@ -331,7 +332,8 @@ public class BubblesTest extends SysuiTestCase {
mPositioner,
mock(DisplayController.class),
syncExecutor,
- mock(Handler.class));
+ mock(Handler.class),
+ mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index e4c78009f491..db8a08cdc5e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -100,6 +100,7 @@ import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -275,7 +276,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mPositioner,
mock(DisplayController.class),
syncExecutor,
- mock(Handler.class));
+ mock(Handler.class),
+ mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index cd5aa9a3f9dc..7b77cb0b5b30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -32,6 +32,7 @@ import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
/**
@@ -54,11 +55,12 @@ public class TestableBubbleController extends BubbleController {
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor shellMainExecutor,
- Handler shellMainHandler) {
+ Handler shellMainHandler,
+ SyncTransactionQueue syncQueue) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
- shellMainExecutor, shellMainHandler);
+ shellMainExecutor, shellMainHandler, syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 5691660b4882..8480702c57c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -41,6 +41,7 @@ import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +70,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
@Mock LegacySplitScreen mLegacySplitScreen;
+ @Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
@@ -81,7 +83,7 @@ public class WMShellTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
- Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
+ Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
@@ -96,8 +98,15 @@ public class WMShellTest extends SysuiTestCase {
}
@Test
+ public void initLegacySplitScreen_registersCallbacks() {
+ mWMShell.initLegacySplitScreen(mLegacySplitScreen);
+
+ verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+ }
+
+ @Test
public void initSplitScreen_registersCallbacks() {
- mWMShell.initSplitScreen(mLegacySplitScreen);
+ mWMShell.initSplitScreen(mSplitScreen);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
}
diff --git a/packages/VpnDialogs/res/values-af/strings.xml b/packages/VpnDialogs/res/values-af/strings.xml
index ac82b0e0009a..88ccbd9e98dc 100644
--- a/packages/VpnDialogs/res/values-af/strings.xml
+++ b/packages/VpnDialogs/res/values-af/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingversoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit sal toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verskyn boaan jou skerm as VPN aktief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verskyn op jou skerm wanneer VPN aktief is."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is gekoppel"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Tydsduur:"</string>
diff --git a/packages/VpnDialogs/res/values-am/strings.xml b/packages/VpnDialogs/res/values-am/strings.xml
index ad9773b248a4..9fc5ff43d946 100644
--- a/packages/VpnDialogs/res/values-am/strings.xml
+++ b/packages/VpnDialogs/res/values-am/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"የግንኙነት ጥያቄ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ መከታተል የሚያስችል የVPN ግንኑነት ማዋቀር ይፈልጋል። ምንጩን የሚያምኑት ብቻ ከሆኑ ይቀበሉ። &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ገቢር ሲሆን በማያ ገጽዎ ላይኛው ክፍል ላይ ይታያል።"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ ትራፊክን ለመቆጣጠር የሚያስችል የVPN ግንኙነትን ማዋቀር ይፈልጋል። ምንጩን የሚያምኑ ከሆነ ብቻ ይቀበሉ። &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ማያ ገጹ ላይ VPN ገቢር ሲሆን ይታያል።"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ተያይዟል"</string>
<string name="session" msgid="6470628549473641030">"ክፍለ ጊዜ፡"</string>
<string name="duration" msgid="3584782459928719435">"ጊዜ"</string>
diff --git a/packages/VpnDialogs/res/values-ar/strings.xml b/packages/VpnDialogs/res/values-ar/strings.xml
index 808cde906d2f..33be6a3e458a 100644
--- a/packages/VpnDialogs/res/values-ar/strings.xml
+++ b/packages/VpnDialogs/res/values-ar/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"طلب الاتصال"</string>
<string name="warning" msgid="809658604548412033">"‏يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بالشبكة الافتراضية الخاصة التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر في الجزء العلوي من الشاشة عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏يريد تطبيق <xliff:g id="APP">%s</xliff:g> إعداد اتصال شبكة افتراضية خاصة (VPN) يتيح له مراقبة حركة بيانات الشبكة. لا تقبل السماح بذلك إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر على شاشتك عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN متصلة"</string>
<string name="session" msgid="6470628549473641030">"الجلسة"</string>
<string name="duration" msgid="3584782459928719435">"المدة:"</string>
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
index 45d8458f4d45..4669a6941cf4 100644
--- a/packages/VpnDialogs/res/values-as/strings.xml
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগৰ অনুৰোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>এ নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ এটা ভিপিএন সংযোগ ছেট আপ কৰিবলৈ বিচাৰিছে৷ আপুনি কেৱল উৎসটোক বিশ্বাস কৰিলেহে অনুৰোধ স্বীকাৰ কৰিব৷ ভিপিএন সক্ৰিয় থকাৰ সময়ত আপোনাৰ স্ক্ৰীণৰ ওপৰত &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দৃশ্যমান হয়৷"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>এ এটা ভিপিএন সংযোগ ছেট আপ কৰিব বিচাৰে, যিটোৱে ইয়াক নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ দিয়ে। আপুনি উৎসটোক বিশ্বাস কৰিলেহে গ্ৰহণ কৰক। ভিপিএনটো সক্ৰিয় হৈ থকাৰ সময়ত আপোনাৰ স্ক্ৰীনত&lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; প্ৰদৰ্শিত হয়।"</string>
<string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
<string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
diff --git a/packages/VpnDialogs/res/values-az/strings.xml b/packages/VpnDialogs/res/values-az/strings.xml
index 2bdf23ee2aa0..d8788350bb8c 100644
--- a/packages/VpnDialogs/res/values-az/strings.xml
+++ b/packages/VpnDialogs/res/values-az/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı Sorğusu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN bağlantı yaratmaq istəyir ki, bu da şəbəkə trafikini izləyə bilər. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. VPN aktiv olan zaman &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ekranın yuxarısında görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> şəbəkə trafikini izləməyə imkan verən VPN bağlantısı yaratmaq istəyir. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN aktiv olan zaman ekranda görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN qoşuludur"</string>
<string name="session" msgid="6470628549473641030">"Sessiya:"</string>
<string name="duration" msgid="3584782459928719435">"Müddət:"</string>
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
index f40e40670bf3..a1075d22df84 100644
--- a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja joj omogućava da prati mrežni saobraćaj. Prihvatite ovo samo ako imate poverenja u izvor. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje na ekranu kada je VPN aktivan."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-be/strings.xml b/packages/VpnDialogs/res/values-be/strings.xml
index 0903c8ece36b..fc3f8787b5d7 100644
--- a/packages/VpnDialogs/res/values-be/strings.xml
+++ b/packages/VpnDialogs/res/values-be/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запыт на падлучэнне"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> спрабуе наладзіць падлучэнне VPN, якое дазваляе сачыць за сеткавым трафікам. Прымайце толькі тады, калі вы давяраеце гэтай крыніцы. Калі VPN актыўны, у верхняй частцы экрана адлюстроўваецца &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" запытвае дазвол на падключэнне да сеткі VPN, каб адсочваць сеткавы трафік. Дайце дазвол, толькі калі вы давяраеце крыніцы. Калі адбудзецца падключэнне да VPN, на экране з\'явіцца значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN падключаны"</string>
<string name="session" msgid="6470628549473641030">"Сессія"</string>
<string name="duration" msgid="3584782459928719435">"Працягласць:"</string>
diff --git a/packages/VpnDialogs/res/values-bg/strings.xml b/packages/VpnDialogs/res/values-bg/strings.xml
index 9ac853d2016f..6345f1dd7911 100644
--- a/packages/VpnDialogs/res/values-bg/strings.xml
+++ b/packages/VpnDialogs/res/values-bg/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Заявка за свързване"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с виртуална частна мрежа (VPN), за да може да наблюдава мрежовия трафик. Приемете само ако източникът е надежден. Иконата &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се показва в долната част на екрана при активирана VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с VPN, за да може да наблюдава трафика в мрежата. Приемете само ако източникът е надежден. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се показва на екрана при активирана VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е свързана"</string>
<string name="session" msgid="6470628549473641030">"Сесия:"</string>
<string name="duration" msgid="3584782459928719435">"Продължителност:"</string>
diff --git a/packages/VpnDialogs/res/values-bn/strings.xml b/packages/VpnDialogs/res/values-bn/strings.xml
index 5e11fd9934b6..352b786bc009 100644
--- a/packages/VpnDialogs/res/values-bn/strings.xml
+++ b/packages/VpnDialogs/res/values-bn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগের অনুরোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট-আপ করতে চাচ্ছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি যদি উৎসটিকে বিশ্বাস করেন, তাহলেই কেবল এতে সম্মতি দিন। VPN সক্রিয় থাকলে আপনার স্ক্রীনের উপরে &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দেখা যাবে।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট আপ করতে চাইছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি সোর্সটি বিশ্বাস করলে একমাত্র তখনই অ্যাক্সেপ্ট করুন। PN অ্যাক্টিভ থাকলে &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; আপনার স্ক্রিনে দেখা যায়।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN সংযুক্ত হয়েছে"</string>
<string name="session" msgid="6470628549473641030">"অধিবেশন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়কাল:"</string>
diff --git a/packages/VpnDialogs/res/values-bs/strings.xml b/packages/VpnDialogs/res/values-bs/strings.xml
index 56812d59e106..fa5f4ea2b762 100644
--- a/packages/VpnDialogs/res/values-bs/strings.xml
+++ b/packages/VpnDialogs/res/values-bs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi podesiti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako je izvor pouzdan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se pojavi na vrhu ekrana kada je VPN aktivna."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako vjerujete izvoru. Kada je VPN aktivan, na ekranu se prikazuje ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN veza uspostavljena"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-ca/strings.xml b/packages/VpnDialogs/res/values-ca/strings.xml
index 97738c316f4b..cdb754723c28 100644
--- a/packages/VpnDialogs/res/values-ca/strings.xml
+++ b/packages/VpnDialogs/res/values-ca/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Sol·licitud de connexió"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti controlar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; es mostra a la part superior de la pantalla quan la VPN està activada."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti monitorar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; és la icona que veuràs a la pantalla quan la VPN estigui activa."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN està connectada"</string>
<string name="session" msgid="6470628549473641030">"Sessió:"</string>
<string name="duration" msgid="3584782459928719435">"Durada:"</string>
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 5cc809c7cb02..c06f6ff0cf02 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žádost o připojení"</string>
<string name="warning" msgid="809658604548412033">"Aplikace <xliff:g id="APP">%s</xliff:g> žádá o nastavení připojení VPN, pomocí kterého bude moci sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; – když je síť VPN aktivní, v horní části obrazovky se zobrazuje tato ikona."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikace <xliff:g id="APP">%s</xliff:g> chce nastavit připojení VPN, které umožňuje sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; – když je síť VPN aktivní, na obrazovce se zobrazuje tato ikona."</string>
<string name="legacy_title" msgid="192936250066580964">"Síť VPN je připojena"</string>
<string name="session" msgid="6470628549473641030">"Relace:"</string>
<string name="duration" msgid="3584782459928719435">"Doba trvání:"</string>
diff --git a/packages/VpnDialogs/res/values-da/strings.xml b/packages/VpnDialogs/res/values-da/strings.xml
index 7641158af3da..a4ddc1963de8 100644
--- a/packages/VpnDialogs/res/values-da/strings.xml
+++ b/packages/VpnDialogs/res/values-da/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Forbindelsesanmodning"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-forbindelse, der giver appen mulighed for at registrere netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises øverst på din skærm, når VPN-forbindelsen er aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> anmoder om at konfigurere en VPN-forbindelse, der giver appen tilladelse til at holde øje med netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til appen. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises på din skærm, når VPN-forbindelsen er aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilsluttet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Varighed:"</string>
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index 0f1e00980439..f38e3953855a 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindungsanfrage"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. Wenn VPN aktiv ist, wird oben im Display &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; angezeigt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wird auf dem Display angezeigt, wenn VPN aktiv ist."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ist verbunden"</string>
<string name="session" msgid="6470628549473641030">"Sitzung:"</string>
<string name="duration" msgid="3584782459928719435">"Dauer:"</string>
diff --git a/packages/VpnDialogs/res/values-el/strings.xml b/packages/VpnDialogs/res/values-el/strings.xml
index 78bcc43ff609..e3eb4603ea61 100644
--- a/packages/VpnDialogs/res/values-el/strings.xml
+++ b/packages/VpnDialogs/res/values-el/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Αίτημα σύνδεσης"</string>
<string name="warning" msgid="809658604548412033">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; εμφανίζεται στο επάνω μέρος της οθόνης σας όταν είναι ενεργό το VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; εμφανίζεται στην οθόνη σας όταν είναι ενεργό το VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Το VPN συνδέθηκε"</string>
<string name="session" msgid="6470628549473641030">"Περίοδος σύνδεσης"</string>
<string name="duration" msgid="3584782459928719435">"Διάρκεια:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rAU/strings.xml b/packages/VpnDialogs/res/values-en-rAU/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rAU/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rAU/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rCA/strings.xml b/packages/VpnDialogs/res/values-en-rCA/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rGB/strings.xml b/packages/VpnDialogs/res/values-en-rGB/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rGB/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rGB/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rIN/strings.xml b/packages/VpnDialogs/res/values-en-rIN/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rIN/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rIN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rXC/strings.xml b/packages/VpnDialogs/res/values-en-rXC/strings.xml
index 9d010e63518f..f5e2deb071a1 100644
--- a/packages/VpnDialogs/res/values-en-rXC/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rXC/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎Connection request‎‏‎‎‏‎"</string>
<string name="warning" msgid="809658604548412033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active.‎‏‎‎‏‎"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active.‎‏‎‎‏‎"</string>
<string name="legacy_title" msgid="192936250066580964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎VPN is connected‎‏‎‎‏‎"</string>
<string name="session" msgid="6470628549473641030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎Session:‎‏‎‎‏‎"</string>
<string name="duration" msgid="3584782459928719435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎Duration:‎‏‎‎‏‎"</string>
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 21cfc042e707..108a24e7930e 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita supervisar el tráfico de red. Solo acéptala si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en tu pantalla cuando se active la VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 372147f2479a..0eaf3592a510 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en la pantalla cuando la VPN esté activa."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-et/strings.xml b/packages/VpnDialogs/res/values-et/strings.xml
index c328cd725396..140c18311607 100644
--- a/packages/VpnDialogs/res/values-et/strings.xml
+++ b/packages/VpnDialogs/res/values-et/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ühendamise taotlus"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; kuvatakse ekraani ülaservas, kui VPN on aktiivne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; kuvatakse ekraanil, kui VPN on aktiivne."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on ühendatud"</string>
<string name="session" msgid="6470628549473641030">"Seansid"</string>
<string name="duration" msgid="3584782459928719435">"Kestus:"</string>
diff --git a/packages/VpnDialogs/res/values-eu/strings.xml b/packages/VpnDialogs/res/values-eu/strings.xml
index a3b7716e91d3..9fc16e27ca7f 100644
--- a/packages/VpnDialogs/res/values-eu/strings.xml
+++ b/packages/VpnDialogs/res/values-eu/strings.xml
@@ -18,13 +18,14 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Konektatzeko eskaera"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. &lt;br /&gt; &lt;br /&gt; VPN konexioa aktibo dagoenean, &lt;img src=vpn_icon /&gt; agertuko da pantailaren goialdean."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexio bat konfiguratu nahi du sareko trafikoa gainbegiratzeko. Onartu soilik iturburuaz fidatzen bazara. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; agertzen da pantailan, VPNa aktibo dagoenean."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN sarera konektatuta dago"</string>
<string name="session" msgid="6470628549473641030">"Saioa:"</string>
<string name="duration" msgid="3584782459928719435">"Iraupena:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"Bidalita:"</string>
<string name="data_received" msgid="4062776929376067820">"Jasota:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> byte / <xliff:g id="NUMBER_1">%2$s</xliff:g> pakete"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPN sarea"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPNa"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP_0">%1$s</xliff:g>, baina une honetan ezin da konektatu. <xliff:g id="VPN_APP_1">%1$s</xliff:g> sarera berriro konektatu ahal izan arte, sare publiko bat erabiliko du telefonoak."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP">%1$s</xliff:g>, baina une honetan ezin da konektatu. VPN sarearen konexioa berreskuratu arte, ez duzu izango konexiorik."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
diff --git a/packages/VpnDialogs/res/values-fa/strings.xml b/packages/VpnDialogs/res/values-fa/strings.xml
index 56f847c15827..6fb5a001316e 100644
--- a/packages/VpnDialogs/res/values-fa/strings.xml
+++ b/packages/VpnDialogs/res/values-fa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"درخواست اتصال"</string>
<string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> می‌خواهد یک اتصال VPN راه‌اندازی کند که به آن امکان نظارت بر ترافیک شبکه را می‌دهد. فقط در صورتی بپذیرید که به منبع آن اطمینان دارید. هنگامی که VPN فعال شد، &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; در بالای صفحه نمایش شما نشان داده می‌شود."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏<xliff:g id="APP">%s</xliff:g> می‌خواهد یک اتصال VPN راه‌اندازی کند که به آن امکان نظارت بر ترافیک شبکه را می‌دهد. فقط درصورتی‌که به منبع اعتماد دارید قبول کنید. وقتی VPN فعال باشد، &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; در صفحه‌نمایش نشان داده می‌شود."</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN متصل است"</string>
<string name="session" msgid="6470628549473641030">"جلسه:"</string>
<string name="duration" msgid="3584782459928719435">"مدت زمان:"</string>
diff --git a/packages/VpnDialogs/res/values-fi/strings.xml b/packages/VpnDialogs/res/values-fi/strings.xml
index 91c918af09c3..8abca06c2739 100644
--- a/packages/VpnDialogs/res/values-fi/strings.xml
+++ b/packages/VpnDialogs/res/values-fi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yhteyspyyntö"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> haluaa tehdä asetukset VPN-yhteydellä, jonka kautta sovellus voi valvoa verkkoliikennettä. Hyväksy vain, jos lähde on luotettava. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; näkyy ruudun yläreunassa, kun VPN on käytössä."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> haluaa muodostaa VPN-yhteyden, jonka avulla se voi valvoa verkkoliikennettä. Salli tämä vain, jos luotat lähteeseen. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; näkyy näytölläsi, kun VPN on aktiivinen."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on yhdistetty"</string>
<string name="session" msgid="6470628549473641030">"Käyttökerta"</string>
<string name="duration" msgid="3584782459928719435">"Kesto:"</string>
diff --git a/packages/VpnDialogs/res/values-fr-rCA/strings.xml b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
index aa86c7ca8a7f..876111c26cdf 100644
--- a/packages/VpnDialogs/res/values-fr-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt;&lt;br /&gt;&lt;img src=vpn_icon/&gt; s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
<string name="legacy_title" msgid="192936250066580964">"RPV connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-fr/strings.xml b/packages/VpnDialogs/res/values-fr/strings.xml
index 71801197ddf2..27ebfb01f098 100644
--- a/packages/VpnDialogs/res/values-fr/strings.xml
+++ b/packages/VpnDialogs/res/values-fr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche en haut de votre écran lorsqu\'une connexion VPN est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche à l\'écran lorsqu\'un VPN est actif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-gl/strings.xml b/packages/VpnDialogs/res/values-gl/strings.xml
index 8a66d081a71b..cd8ee8d89474 100644
--- a/packages/VpnDialogs/res/values-gl/strings.xml
+++ b/packages/VpnDialogs/res/values-gl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitude de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permite controlar o tráfico da rede. Acepta soamente se confías na fonte. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na parte superior da pantalla cando se activa a VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A aplicación <xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permita supervisar o tráfico de rede. Acepta só se confías nela. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na pantalla cando a VPN está activa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-gu/strings.xml b/packages/VpnDialogs/res/values-gu/strings.xml
index 961711c57c3d..b5a88310fb43 100644
--- a/packages/VpnDialogs/res/values-gu/strings.xml
+++ b/packages/VpnDialogs/res/values-gu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"કનેક્શન વિનંતી"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN કનેક્શન સેટ કરવા માગે છે જે તેને નેટવર્ક ટ્રાફિક મૉનિટર કરવાની મંજૂરી આપે છે. જો તમને સ્રોત પર વિશ્વાસ હોય તો જ સ્વીકારો. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; તમારી સ્ક્રીનની ટોચ પર ત્યારે દેખાય છે જ્યારે VPN સક્રિય હોય છે."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> એક એવું VPN કનેક્શન સેટ કરવા માગે છે કે જે તેને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરવાની મંજૂરી આપતું હોય. જો તમને સૉર્સ પર વિશ્વાસ હોય તો જ સ્વીકારો. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; તમારી સ્ક્રીન પર ત્યારે દેખાય છે, જ્યારે VPN સક્રિય હોય છે."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN કનેક્ટ કરેલું છે"</string>
<string name="session" msgid="6470628549473641030">"સત્ર:"</string>
<string name="duration" msgid="3584782459928719435">"અવધિ:"</string>
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index eed0858787d9..c9c65d5ac593 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्‍शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; आपकी स्क्रीन के सबसे ऊपर दिखाई देता है."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> को वीपीएन कनेक्शन सेट अप करने की अनुमति चाहिए. इससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. अनुमति तब दें, जब आपको ऐप्लिकेशन पर भरोसा हो. वीपीएन चालू होने पर, आपकी स्क्रीन पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिखेगा."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट है"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-hr/strings.xml b/packages/VpnDialogs/res/values-hr/strings.xml
index aa9e436f56e7..576d99761571 100644
--- a/packages/VpnDialogs/res/values-hr/strings.xml
+++ b/packages/VpnDialogs/res/values-hr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kada je VPN aktivan, pri vrhu zaslona prikazuje se &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kad je VPN aktivan, na zaslonu se prikazuje ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je spojen"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-hu/strings.xml b/packages/VpnDialogs/res/values-hu/strings.xml
index 703aa792f3c3..69b999fee1e1 100644
--- a/packages/VpnDialogs/res/values-hu/strings.xml
+++ b/packages/VpnDialogs/res/values-hu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kapcsolódási kérés"</string>
<string name="warning" msgid="809658604548412033">"A(z) <xliff:g id="APP">%s</xliff:g> VPN kapcsolatot akar beállítani, amelynek segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. &lt;br /&gt; &lt;br /&gt; Amikor a VPN aktív, &lt;img src=vpn_icon /&gt; ikon jelenik meg a képernyő tetején."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás VPN-kapcsolatot szeretne beállítani, amely segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. &lt;br /&gt; &lt;br /&gt; Amikor aktív a VPN, a következő ikon látható a képernyőn: &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN csatlakoztatva van"</string>
<string name="session" msgid="6470628549473641030">"Munkamenet:"</string>
<string name="duration" msgid="3584782459928719435">"Időtartam:"</string>
diff --git a/packages/VpnDialogs/res/values-hy/strings.xml b/packages/VpnDialogs/res/values-hy/strings.xml
index c296c8547283..d2a6d421592c 100644
--- a/packages/VpnDialogs/res/values-hy/strings.xml
+++ b/packages/VpnDialogs/res/values-hy/strings.xml
@@ -17,7 +17,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Միացման հայց"</string>
- <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին: Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերը:"</string>
+ <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերը:"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> հավելվածն ուզում է միանալ VPN-ի ցանցին՝ թրաֆիկին հետևելու համար։ Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվացված լինի, &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերակը կհայտնվի ձեր էկրանին։"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-ը կապակցված է"</string>
<string name="session" msgid="6470628549473641030">"Աշխատաշրջան`"</string>
<string name="duration" msgid="3584782459928719435">"Տևողությունը՝"</string>
@@ -25,7 +26,7 @@
<string name="data_received" msgid="4062776929376067820">"Ստացվել է՝"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> բայթ / <xliff:g id="NUMBER_1">%2$s</xliff:g> փաթեթ"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Չի հաջողվում միանալ միշտ միացված VPN-ին"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին:"</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին։"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Մինչև VPN-ը նորից չմիանա, դուք կապ չեք ունենա:"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Փոխել VPN-ի կարգավորումները"</string>
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index 18ef372a8cda..059008f3674e 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan sambungan VPN yang memungkinkannya memantau traffic jaringan. Terima hanya jika Anda memercayai sumber. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul di bagian atas layar Anda saat VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan koneksi VPN yang memungkinkannya memantau traffic jaringan. Hanya terima jika Anda memercayai sumbernya. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul di layar bila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN tersambung"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Durasi:"</string>
diff --git a/packages/VpnDialogs/res/values-is/strings.xml b/packages/VpnDialogs/res/values-is/strings.xml
index 70fb40fc467c..a75371d77951 100644
--- a/packages/VpnDialogs/res/values-is/strings.xml
+++ b/packages/VpnDialogs/res/values-is/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Beiðni um tengingu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til þess að geta fylgst með netumferð. Samþykktu þetta aðeins ef þú treystir upprunanum. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; birtist efst á skjánum þegar VPN er virkt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til að fylgjast með netumferð. Ekki samþykkja þú treystir upprunanum. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; birtist á skjánum hjá þér þegar VPN er virkt."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tengt"</string>
<string name="session" msgid="6470628549473641030">"Lota:"</string>
<string name="duration" msgid="3584782459928719435">"Tímalengd:"</string>
diff --git a/packages/VpnDialogs/res/values-it/strings.xml b/packages/VpnDialogs/res/values-it/strings.xml
index 2602493faf00..c443c510198e 100644
--- a/packages/VpnDialogs/res/values-it/strings.xml
+++ b/packages/VpnDialogs/res/values-it/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Richiesta di connessione"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vuole impostare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, nella parte superiore dello schermo viene visualizzata l\'icona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vuole configurare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, sullo schermo viene visualizzata l\'icona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connessa"</string>
<string name="session" msgid="6470628549473641030">"Sessione:"</string>
<string name="duration" msgid="3584782459928719435">"Durata:"</string>
diff --git a/packages/VpnDialogs/res/values-iw/strings.xml b/packages/VpnDialogs/res/values-iw/strings.xml
index ebabd4e71aef..81903d2b2442 100644
--- a/packages/VpnDialogs/res/values-iw/strings.xml
+++ b/packages/VpnDialogs/res/values-iw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"בקשת חיבור"</string>
<string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> רוצה להגדיר חיבור VPN שיאפשר לו לפקח על תעבורת הרשת. אשר את הבקשה רק אם אתה נותן אמון במקור. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; מופיע בחלק העליון של המסך כאשר VPN פעיל."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏האפליקציה <xliff:g id="APP">%s</xliff:g> מבקשת להגדיר חיבור VPN שבאמצעותו היא תנהל מעקב אחר התנועה ברשת. יש לאשר את הבקשה רק אם המקור נראה לך אמין. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; מופיע על המסך כאשר חיבור ה-VPN פעיל."</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN מחובר"</string>
<string name="session" msgid="6470628549473641030">"הפעלה"</string>
<string name="duration" msgid="3584782459928719435">"משך:"</string>
diff --git a/packages/VpnDialogs/res/values-ja/strings.xml b/packages/VpnDialogs/res/values-ja/strings.xml
index 8480692e9dd3..e03e9d38087b 100644
--- a/packages/VpnDialogs/res/values-ja/strings.xml
+++ b/packages/VpnDialogs/res/values-ja/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"接続リクエスト"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> がネットワーク トラフィックを監視するため VPN 接続をセットアップしようとしています。信頼できるソースである場合にのみ許可してください。&lt;br /&gt; &lt;br /&gt; VPN がアクティブになると画面の上部に &lt;img src=vpn_icon /&gt; が表示されます。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> は、ネットワーク トラフィックを監視できるよう、VPN 接続を設定するよう求めています。ソースを信頼できる場合のみ、許可してください。VPN が有効になると、画面に &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; が表示されます。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN接続済み"</string>
<string name="session" msgid="6470628549473641030">"セッション:"</string>
<string name="duration" msgid="3584782459928719435">"期間:"</string>
diff --git a/packages/VpnDialogs/res/values-ka/strings.xml b/packages/VpnDialogs/res/values-ka/strings.xml
index e5a07532c32e..9c4388e0e30e 100644
--- a/packages/VpnDialogs/res/values-ka/strings.xml
+++ b/packages/VpnDialogs/res/values-ka/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"კავშირის მოთხოვნა"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. მიიღოთ მხოლოდ ისეთ შემთხვევაში, თუ წყაროს ენდობით. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; თქვენი ეკრანის სიის თავში გამოჩნდება, როდესაც VPN აქტიურია."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>-ს სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. დათანხმდით მხოლოდ იმ შემთხვევაში, თუ წყაროს ენდობით. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; თქვენს ეკრანზე გამოჩნდება, როდესაც VPN აქტიურია."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN დაკავშირებულია"</string>
<string name="session" msgid="6470628549473641030">"სესია:"</string>
<string name="duration" msgid="3584782459928719435">"ხანგრძლივობა:"</string>
diff --git a/packages/VpnDialogs/res/values-kk/strings.xml b/packages/VpnDialogs/res/values-kk/strings.xml
index 79f79c34e1b4..9a499d346ef7 100644
--- a/packages/VpnDialogs/res/values-kk/strings.xml
+++ b/packages/VpnDialogs/res/values-kk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Байланысты сұрау"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN байланысын орнатқысы келеді, бұл оған желілік трафикті бақылауға мүмкіндік береді. Көзге сенсеңіз ғана қабылдаңыз. VPN белсенді болғанда экранның жоғарғы жағында &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; көрсетіледі."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> қолданбасы VPN байланысын орнатқысы келеді, бұл оған желі трафигін бақылауға мүмкіндік береді. Сұрауды қабылдамас бұрын, дереккөздің сенімді екеніне көз жеткізіңіз. VPN белсенді болған кезде, экранда &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; белгішесі пайда болады."</string>
<string name="legacy_title" msgid="192936250066580964">"ВЖЖ қосылған"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Ұзақтығы:"</string>
diff --git a/packages/VpnDialogs/res/values-km/strings.xml b/packages/VpnDialogs/res/values-km/strings.xml
index 06f34dbf2733..0ed2e84b80fa 100644
--- a/packages/VpnDialogs/res/values-km/strings.xml
+++ b/packages/VpnDialogs/res/values-km/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"សំណើ​សុំ​ការ​តភ្ជាប់"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ចង់​បង្កើត​ការ​តភ្ជាប់ VPN ដែល​អនុញ្ញាត​ឲ្យ​វា​ត្រួតពិនិត្យ​ចរាចរ​បណ្ដាញ។ ព្រម​ទទួល ប្រសិនបើ​អ្នក​ទុកចិត្ត​លើ​ប្រភព​តែប៉ុណ្ណោះ។ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; នឹង​លេចឡើង​នៅ​ផ្នែក​ខាងលើ​នៃ​អេក្រង់​របស់​អ្នក ពេល VPN សកម្ម។"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ចង់រៀបចំការតភ្ជាប់ VPN ដែលអនុញ្ញាតឱ្យវាត្រួតពិនិត្យ​ចរាចរណ៍បណ្តាញ។ យល់ព្រម ប្រសិនបើអ្នកជឿទុកចិត្តលើប្រភពនេះតែប៉ុណ្ណោះ។ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; បង្ហាញនៅលើអេក្រង់របស់អ្នក នៅពេល VPN កំពុងដំណើរការ។"</string>
<string name="legacy_title" msgid="192936250066580964">"បា​ន​ភ្ជាប់ VPN"</string>
<string name="session" msgid="6470628549473641030">"សម័យ៖"</string>
<string name="duration" msgid="3584782459928719435">"ថិរវេលា៖"</string>
diff --git a/packages/VpnDialogs/res/values-kn/strings.xml b/packages/VpnDialogs/res/values-kn/strings.xml
index 040cd6c5aeda..6308f1844bfd 100644
--- a/packages/VpnDialogs/res/values-kn/strings.xml
+++ b/packages/VpnDialogs/res/values-kn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ಸಂಪರ್ಕ ವಿನಂತಿ"</string>
<string name="warning" msgid="809658604548412033">"ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನೀವು ಮೂಲವನ್ನು ನಂಬಿದರೆ ಮಾತ್ರ ಸಮ್ಮತಿಸಿ. VPN ಸಕ್ರಿಯವಾಗಿರುವಾಗ ನಿಮ್ಮ ಪರದೆಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ಗೋರಿಸುತ್ತದೆ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನಿಮಗೆ ಮೂಲದ ಮೇಲೆ ನಂಬಿಕೆ ಇದ್ದರೆ ಮಾತ್ರ ಸ್ವೀಕರಿಸಿ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ಸಕ್ರಿಯವಾದ ನಂತರ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುತ್ತದೆ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="session" msgid="6470628549473641030">"ಸೆಷನ್:"</string>
<string name="duration" msgid="3584782459928719435">"ಅವಧಿ:"</string>
diff --git a/packages/VpnDialogs/res/values-ko/strings.xml b/packages/VpnDialogs/res/values-ko/strings.xml
index 6ad497680ae7..6e179bb9bcb0 100644
--- a/packages/VpnDialogs/res/values-ko/strings.xml
+++ b/packages/VpnDialogs/res/values-ko/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"연결 요청"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링하도록 허용하는 VPN 연결을 설정하려고 합니다. 출처를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성화되면 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;이 화면 위에 표시됩니다."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링할 수 있도록 VPN 연결을 설정하려고 합니다. 소스를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성 상태일 때는 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; 아이콘이 화면에 표시됩니다."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN이 연결되었습니다."</string>
<string name="session" msgid="6470628549473641030">"세션:"</string>
<string name="duration" msgid="3584782459928719435">"기간:"</string>
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 23c9be8819a8..31f9e2da11c7 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Туташуу сурамы"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> тармактык трафикти көзөмөлдөөгө уруксат берген VPN туташуусун орноткусу келет. Аны булакка ишенсеңиз гана кабыл алыңыз. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN иштеп турганда экраныңыздын жогору жагынан көрүнөт."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> тармак трафигин көзөмөлдөөгө уруксат берген VPN байланышын орноткусу келет. Булакка ишенсеңиз гана кабыл алыңыз. VPN иштеп жатканда, экраныңызда &lt;br /&gt; &lt;br /&gt; &amp;It;img src=vpn_icon /&gt; көрүнөт."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN байланышта"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Узактыгы:"</string>
diff --git a/packages/VpnDialogs/res/values-lo/strings.xml b/packages/VpnDialogs/res/values-lo/strings.xml
index c591308480c1..cec69f0fe9c0 100644
--- a/packages/VpnDialogs/res/values-lo/strings.xml
+++ b/packages/VpnDialogs/res/values-lo/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ການ​ຮ້ອງ​ຂໍ​ການ​ເຊື່ອມ​ຕໍ່"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງ​ການ​ຕັ້​ງ​ຄ່າ​ການ​ເຊື່ອມ​ຕໍ່ VPN ທີ່​ອະ​ນຸ​ຍາດ​ໃຫ້​ຕິດ​ຕາມ​ທຣາບ​ຟິກ​ເຄືອ​ຂ່າຍ​ໄດ້. ​ທ່ານ​​ຄວນ​​ຍິນຍອມ​ສະ​ເພາະ​ໃນ​ກໍ​ລະ​ນີ​ທີ່​ທ່ານ​ເຊື່ອ​ຖື​ແຫລ່ງ​ຂໍ້​ມູນ​ເທົ່າ​ນັ້ນ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ຈະ​ປາ​ກົດ​ຢູ່​ດ້ານ​ເທິງ​ຂອງ​ໜ້າ​ຈໍ​ເມື່ອ​ມີ​ການ​ເປີດ​ໃຊ້ VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ມັນກວດກາການ​ຈາ​ລະ​ຈອນ​ເຄືອ​ຂ່າ​ຍໄດ້. ໃຫ້ຍອມຮັບສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫຼ່ງທີ່ມາເທົ່ານັ້ນ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ຈະປາກົດຢູ່ໜ້າຈໍຂອງທ່ານເມື່ອເປີດໃຊ້ VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
<string name="session" msgid="6470628549473641030">"ເຊສຊັນ:"</string>
<string name="duration" msgid="3584782459928719435">"ໄລຍະເວລາ:"</string>
diff --git a/packages/VpnDialogs/res/values-lt/strings.xml b/packages/VpnDialogs/res/values-lt/strings.xml
index 8846310730ce..97abd0d66eb3 100644
--- a/packages/VpnDialogs/res/values-lt/strings.xml
+++ b/packages/VpnDialogs/res/values-lt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ryšio užklausa"</string>
<string name="warning" msgid="809658604548412033">"„<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; rodoma ekrano viršuje, kai VPN aktyvus."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Programa „<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; piktograma rodoma ekrane, kai VPN aktyvus."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN prijungtas"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trukmė:"</string>
diff --git a/packages/VpnDialogs/res/values-lv/strings.xml b/packages/VpnDialogs/res/values-lv/strings.xml
index 07625b6173c6..6341fbdf0158 100644
--- a/packages/VpnDialogs/res/values-lv/strings.xml
+++ b/packages/VpnDialogs/res/values-lv/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Savienojuma pieprasījums"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; tiek rādīta ekrāna augšdaļā, kad darbojas VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Lietotne <xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. &lt;br /&gt; &lt;br /&gt; Ekrānā tiek rādīta ikona &lt;img src=vpn_icon /&gt;, kad darbojas VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Ir izveidots savienojums ar VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Ilgums:"</string>
diff --git a/packages/VpnDialogs/res/values-mk/strings.xml b/packages/VpnDialogs/res/values-mk/strings.xml
index b5a64f213066..689d028fd724 100644
--- a/packages/VpnDialogs/res/values-mk/strings.xml
+++ b/packages/VpnDialogs/res/values-mk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Барање за поврзување"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со ВПН коешто му дозволува да го набљудува сообраќајот на мрежата. Прифатете само доколку му верувате на изворот. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се појавува на врвот на екранот кога ВПН е активна."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со VPN што ќе дозволи да го набљудува сообраќајот на мрежата. Прифатете само ако му верувате на изворот. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ќе се појави на екранот кога ќе се активира VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е поврзана"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Времетраење:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Променете ги поставките за VPN"</string>
<string name="configure" msgid="4905518375574791375">"Конфигурирај"</string>
- <string name="disconnect" msgid="971412338304200056">"Исклучи"</string>
+ <string name="disconnect" msgid="971412338304200056">"Прекини врска"</string>
<string name="open_app" msgid="3717639178595958667">"Отвори ја апликацијата"</string>
<string name="dismiss" msgid="6192859333764711227">"Отфрли"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-ml/strings.xml b/packages/VpnDialogs/res/values-ml/strings.xml
index 680d0ef539b7..8284a78c26f8 100644
--- a/packages/VpnDialogs/res/values-ml/strings.xml
+++ b/packages/VpnDialogs/res/values-ml/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"കണക്ഷൻ അഭ്യർത്ഥന"</string>
<string name="warning" msgid="809658604548412033">"നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ <xliff:g id="APP">%s</xliff:g> സജ്ജീകരിക്കേണ്ടതുണ്ട്. ഉറവിടം പരിചിതമാണെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; നിങ്ങളുടെ സ്ക്രീനിന്റെ മുകളിൽ ദൃശ്യമാകുന്നു."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"നെറ്റ്‌വർക്ക് ട്രാഫിക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ സജ്ജീകരിക്കാൻ <xliff:g id="APP">%s</xliff:g> താൽപ്പര്യപ്പെടുന്നു. നിങ്ങൾ ഉറവിടം വിശ്വസിക്കുന്നുണ്ടെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകും."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN കണക്‌റ്റുചെയ്‌തു"</string>
<string name="session" msgid="6470628549473641030">"സെഷൻ:"</string>
<string name="duration" msgid="3584782459928719435">"സമയദൈര്‍ഘ്യം:"</string>
diff --git a/packages/VpnDialogs/res/values-mn/strings.xml b/packages/VpnDialogs/res/values-mn/strings.xml
index 9aa104aff5ab..1dd4c15c43bb 100644
--- a/packages/VpnDialogs/res/values-mn/strings.xml
+++ b/packages/VpnDialogs/res/values-mn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Холболтын хүсэлт"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> нь сүлжээний трафикыг хянах боломж бүхий VPN холболт үүсгэхийг хүсэж байна. Та зөвхөн эх үүсвэрт итгэж байгаа бол зөвшөөрнө үү. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; таны дэлгэц дээр VPN идэвхтэй үед гарч ирнэ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> нь түүнд сүлжээний ачааллыг хянах боломжийг олгодог VPN холболт тохируулахыг хүсэж байна. Зөвхөн та эх сурвалжид итгэдэг тохиолдолд зөвшөөрнө үү. VPN идэвхтэй үед таны дэлгэц дээр &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; харагдана."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN холбогдов"</string>
<string name="session" msgid="6470628549473641030">"Сешн:"</string>
<string name="duration" msgid="3584782459928719435">"Үргэлжлэх хугацаа:"</string>
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index 41d74290815d..22fb502129a5 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -18,18 +18,19 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्‍शन विनंती"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्‍यासाठी त्यास अनुमती देणारे VPN कनेक्‍शन सेट करू इच्‍छितो. तुम्हाला स्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN सक्रिय असताना आपल्‍या स्क्रीनच्या शीर्षावर दिसते."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ला नेटवर्क ट्रॅफिकवर लक्ष ठेवण्याची अनुमती देणारे VPN कनेक्‍शन सेट करायचे आहे. तुमचा स्रोतावर विश्वास असेल तरच स्वीकारा. VPN अ‍ॅक्टिव्ह असल्यास, तुमच्या स्क्रीनवर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिसते."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट केले"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"कालावधी:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"प्रेषित:"</string>
<string name="data_received" msgid="4062776929376067820">"प्राप्त झाले:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइट / <xliff:g id="NUMBER_1">%2$s</xliff:g> पॅकेट"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम चालू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम सुरू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. <xliff:g id="VPN_APP_1">%1$s</xliff:g> शी पुन्हा कनेक्ट होईपर्यंत तुमचा फोन सार्वजनिक नेटवर्क वापरेल."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
- <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
+ <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
<string name="disconnect" msgid="971412338304200056">"‍डिस्कनेक्ट करा"</string>
<string name="open_app" msgid="3717639178595958667">"अ‍ॅप उघडा"</string>
<string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/VpnDialogs/res/values-ms/strings.xml b/packages/VpnDialogs/res/values-ms/strings.xml
index b489f2edabc0..c9961d2654e2 100644
--- a/packages/VpnDialogs/res/values-ms/strings.xml
+++ b/packages/VpnDialogs/res/values-ms/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl memantau trafik rangkaian. Terima hanya jika anda mempercayai sumber. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; terpapar pada bahagian atas skrin anda apabila VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl tersebut memantau trafik rangkaian. Hanya terima jika anda mempercayai sumber tersebut. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul pada skrin anda apabila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN telah disambungkan"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Tempoh:"</string>
diff --git a/packages/VpnDialogs/res/values-my/strings.xml b/packages/VpnDialogs/res/values-my/strings.xml
index 9d60ff42a7cd..36348c8b5c8c 100644
--- a/packages/VpnDialogs/res/values-my/strings.xml
+++ b/packages/VpnDialogs/res/values-my/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ချိတ်ဆက်ရန် တောင်းဆိုချက်"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက် လုပ်ငန်းကို စောင့်ကြည့်ခွင့် ပြုမည့် VPN ချိတ်ဆက်မှုကို ထူထောင်လိုသည်။ ရင်းမြစ်ကို သင်က ယုံကြည်မှသာ လက်ခံပါ။ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; မှာ VPN အလုပ်လုပ်နေလျှင် သင်၏ မျက်နှာပြင် ထိပ်မှာ ပေါ်လာမည်။"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်ရန် ခွင့်ပြုသည့် VPN ချိတ်ဆက်မှုကို စနစ်ထည့်သွင်းလိုသည်။ ဤရင်းမြစ်ကို သင်ယုံကြည်မှသာ လက်ခံပါ။ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; သည် VPN ဖွင့်ထားသောအခါ သင့်ဖန်သားပြင်တွင် ပေါ်ပါသည်။"</string>
<string name="legacy_title" msgid="192936250066580964">"VPNနှင့်ချိတ်ဆက်ထားသည်"</string>
<string name="session" msgid="6470628549473641030">"သတ်မှတ်ပေးထားသည့်အချိန်:"</string>
<string name="duration" msgid="3584782459928719435">"အချိန်ကာလ-"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ဆက်တင်များ ပြောင်းရန်"</string>
<string name="configure" msgid="4905518375574791375">"ပုံပေါ်စေသည်"</string>
- <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်ခြင်းရပ်ရန်"</string>
+ <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="open_app" msgid="3717639178595958667">"အက်ပ်ကို ဖွင့်ရန်"</string>
<string name="dismiss" msgid="6192859333764711227">"ပယ်ရန်"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml
index be572d4408f8..14c84d702712 100644
--- a/packages/VpnDialogs/res/values-nb/strings.xml
+++ b/packages/VpnDialogs/res/values-nb/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Tilkoblingsforespørsel"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ønsker å bruke en VPN-tilkobling som tillater at appen overvåker nettverkstrafikken. Du bør bare godta dette hvis du stoler på kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises øverst på skjermen din når VPN er aktivert."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-tilkobling som lar appen overvåke nettverkstrafikk. Du bør bare godta dette hvis du stoler på kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises på skjermen når VPN er aktivert."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilkoblet"</string>
<string name="session" msgid="6470628549473641030">"Økt:"</string>
<string name="duration" msgid="3584782459928719435">"Varighet:"</string>
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index b716c35cfad4..2a5648d147c1 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"जडान अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ले नेटवर्क यातायात अनुगमन गर्न अनुमति दिने VPN जडान स्थापना गर्न चाहन्छ। तपाईँले स्रोत भरोसा छ भने मात्र स्वीकार गर्नुहोस्। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; जब VPN सक्रिय हुन्छ आफ्नो स्क्रिनको माथि देखा पर्छन्।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ले कुनै VPN कनेक्सन सेटअप गर्न चाहन्छ। यसको सहायताले यो एप नेटवर्क ट्राफिकको निगरानी राख्न सक्छ। तपाईं यो एपमाथि विश्वास गर्नुहुन्छ भने मात्र स्वीकार गर्नुहोस्। VPN सक्रिय हुँदा तपाईंको स्क्रिनमा &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; देखा पर्छ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN जोडिएको छ"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
<string name="configure" msgid="4905518375574791375">"कन्फिगर गर्नुहोस्"</string>
- <string name="disconnect" msgid="971412338304200056">"विच्छेदन गर्नुहोस्"</string>
- <string name="open_app" msgid="3717639178595958667">"अनुप्रयोग खोल्नुहोस्"</string>
+ <string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट गर्नुहोस्"</string>
+ <string name="open_app" msgid="3717639178595958667">"एप खोल्नुहोस्"</string>
<string name="dismiss" msgid="6192859333764711227">"खारेज गर्नुहोस्"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 8073b09e203c..33f8a89a1562 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verschijnt op je scherm als het VPN actief is."</string>
<string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Duur:"</string>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
index f1122ebd4386..0604b47cdb50 100644
--- a/packages/VpnDialogs/res/values-or/strings.xml
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ସଂଯୋଗ ଅନୁରୋଧ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହି ନେଟ୍‌ୱର୍କର ଟ୍ରାଫିକକୁ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦିଏ। ଆପଣ ସୋର୍ସ ଉପରେ ବିଶ୍ୱାସ କରିବା ବଦଳରେ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ସକ୍ରିୟ ଥିବାବେଳେ ଏହା ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍‍ର ଉପରେ ଦେଖାଯାଏ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହାକୁ ନେଟୱାର୍କ ଟ୍ରାଫିକ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଯଦି ଆପଣ ସୋର୍ସରେ ବିଶ୍ୱାସ କରୁଛନ୍ତି, ତେବେ ହିଁ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ସକ୍ରିୟ ଥିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ ଉପରେ ଦେଖାଯାଏ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ସଂଯୋଗ ହେଲା"</string>
<string name="session" msgid="6470628549473641030">"ସେସନ୍‍:"</string>
<string name="duration" msgid="3584782459928719435">"ଅବଧି:"</string>
@@ -28,7 +29,7 @@
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇ ରହିବା ପାଇଁ ସେଟଅପ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ଫୋନ୍‍, <xliff:g id="VPN_APP_1">%1$s</xliff:g> ସହ କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଏକ ପବ୍ଲିକ୍‍ ନେଟ୍‌ୱର୍କ ବ୍ୟବହାର କରିବ।"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇରହିବାକୁ ସେଟଅପ୍‍ କରାଯାଇଛି, କିନ୍ତୁ ଏହା ବର୍ତ୍ତମାନ କନେକ୍ଟ କରିପାରୁ ନାହିଁ। VPN ପୁଣି କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଆପଣଙ୍କର କୌଣସି କନେକ୍ସନ୍‌ ରହିବନାହିଁ।"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଙ୍ଗ ବଦଳାନ୍ତୁ"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଂସ୍ ବଦଳାନ୍ତୁ"</string>
<string name="configure" msgid="4905518375574791375">"କନଫିଗର୍‍ କରନ୍ତୁ"</string>
<string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="open_app" msgid="3717639178595958667">"ଆପ୍‌ ଖୋଲନ୍ତୁ"</string>
diff --git a/packages/VpnDialogs/res/values-pa/strings.xml b/packages/VpnDialogs/res/values-pa/strings.xml
index 1815f4fb0d25..d2eba0f52005 100644
--- a/packages/VpnDialogs/res/values-pa/strings.xml
+++ b/packages/VpnDialogs/res/values-pa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ਕਨੈਕਸ਼ਨ ਬੇਨਤੀ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ਇੱਕ VPN ਕਨੈਕਸ਼ਨ ਸੈਟ ਅਪ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟ੍ਰੈਫਿਕ ਦਾ ਨਿਰੀਖਣ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਕੇਵਲ ਤਾਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇਕਰ ਤੁਸੀਂ ਸਰੋਤ ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਟੌਪ ਤੇ ਪ੍ਰਗਟ ਹੁੰਦਾ ਹੈ ਜਦੋਂ VPN ਸਕਿਰਿਆ ਹੁੰਦਾ ਹੈ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ਕਿਸੇ ਅਜਿਹੇ VPN ਕਨੈਕਸ਼ਨ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ। ਸਿਰਫ਼ ਉਦੋਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇ ਤੁਹਾਨੂੰ ਸਰੋਤ \'ਤੇ ਭਰੋਸਾ ਹੈ। VPN ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ \'ਤੇ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਦਾ ਹੈ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="session" msgid="6470628549473641030">"ਸੈਸ਼ਨ:"</string>
<string name="duration" msgid="3584782459928719435">"ਮਿਆਦ:"</string>
diff --git a/packages/VpnDialogs/res/values-pl/strings.xml b/packages/VpnDialogs/res/values-pl/strings.xml
index d5201d7fbdf5..82161d389368 100644
--- a/packages/VpnDialogs/res/values-pl/strings.xml
+++ b/packages/VpnDialogs/res/values-pl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Żądanie połączenia"</string>
<string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. &lt;br /&gt; &lt;br /&gt;Gdy sieć VPN jest aktywna, u góry ekranu pojawia się &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu w sieci. Zaakceptuj, jeśli masz zaufanie do źródła. &lt;br /&gt; &lt;br Gdy sieć VPN jest aktywna, na ekranie pojawia się ikona /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Połączono z VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesja:"</string>
<string name="duration" msgid="3584782459928719435">"Czas trwania:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rBR/strings.xml b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
index 75c140617cf5..0d6dd0b136e9 100644
--- a/packages/VpnDialogs/res/values-pt-rBR/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rPT/strings.xml b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
index 01beddbab4e4..a310104555a2 100644
--- a/packages/VpnDialogs/res/values-pt-rPT/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Pedido de ligação"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na fonte. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na parte superior do seu ecrã quando a VPN está ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A app <xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece no ecrã quando a VPN está ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está ligada"</string>
<string name="session" msgid="6470628549473641030">"Sessão"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
@@ -25,12 +26,12 @@
<string name="data_received" msgid="4062776929376067820">"Recebidos:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bytes / <xliff:g id="NUMBER_1">%2$s</xliff:g> pacotes"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Não é possível estabelecer ligação à VPN sempre ativada"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"A aplicação <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à aplicação <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
- <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A aplicação <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"A app <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à app <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
+ <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A app <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Alterar as definições da VPN"</string>
<string name="configure" msgid="4905518375574791375">"Configurar"</string>
<string name="disconnect" msgid="971412338304200056">"Desligar"</string>
- <string name="open_app" msgid="3717639178595958667">"Abrir aplicação"</string>
+ <string name="open_app" msgid="3717639178595958667">"Abrir app"</string>
<string name="dismiss" msgid="6192859333764711227">"Ignorar"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-pt/strings.xml b/packages/VpnDialogs/res/values-pt/strings.xml
index 75c140617cf5..0d6dd0b136e9 100644
--- a/packages/VpnDialogs/res/values-pt/strings.xml
+++ b/packages/VpnDialogs/res/values-pt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 4e60df2eca8e..5bda87e5c257 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se afișează în partea de sus a ecranului."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptați numai dacă aveți încredere în sursă. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; va apărea pe ecran atunci când conexiunea VPN este activă."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string>
<string name="session" msgid="6470628549473641030">"Sesiune:"</string>
<string name="duration" msgid="3584782459928719435">"Durată:"</string>
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index f8fcfb83aa9a..ce099562f854 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
<string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /><br />Когда подключение к сети VPN активно, в верхней части экрана появляется значок &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. Когда подключение к сети VPN активно, на экране появляется значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-si/strings.xml b/packages/VpnDialogs/res/values-si/strings.xml
index bb97a5d86c5f..a836baef545c 100644
--- a/packages/VpnDialogs/res/values-si/strings.xml
+++ b/packages/VpnDialogs/res/values-si/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"සම්බන්ධතා ඉල්ලීම"</string>
<string name="warning" msgid="809658604548412033">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> අවශ්‍යය වේ. ප්‍රභවය ඔබ විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. VPN සක්‍රිය විට &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> හට අවශ්‍ය වේ. ඔබ මූලාශ්‍රය විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN සක්‍රිය විට ඔබගේ තිරයෙහි දිස් වේ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN සම්බන්ධිතයි"</string>
<string name="session" msgid="6470628549473641030">"සැසිය:"</string>
<string name="duration" msgid="3584782459928719435">"කාල සීමාව:"</string>
diff --git a/packages/VpnDialogs/res/values-sk/strings.xml b/packages/VpnDialogs/res/values-sk/strings.xml
index a08117adfac1..766c1393b524 100644
--- a/packages/VpnDialogs/res/values-sk/strings.xml
+++ b/packages/VpnDialogs/res/values-sk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žiadosť o pripojenie"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> žiada o nastavenie pripojenia VPN, pomocou ktorého bude môcť sledovať sieťové prenosy. Povoľte iba v prípade, že zdroju dôverujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa zobrazuje v hornej časti obrazovky, keď je pripojenie VPN aktívne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce nastaviť pripojenie k sieti VPN, ktoré jej umožňuje sledovať sieťovú premávku. Povoľte to iba v prípade, ak zdroju dôverujete. &lt;br /&gt; &lt;br /&gt; Keď je sieť VPN aktívna, na obrazovke sa zobrazí ikona &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Sieť VPN je pripojená"</string>
<string name="session" msgid="6470628549473641030">"Relácia"</string>
<string name="duration" msgid="3584782459928719435">"Trvanie:"</string>
diff --git a/packages/VpnDialogs/res/values-sl/strings.xml b/packages/VpnDialogs/res/values-sl/strings.xml
index d5014fa34394..361a5fa2f1fa 100644
--- a/packages/VpnDialogs/res/values-sl/strings.xml
+++ b/packages/VpnDialogs/res/values-sl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahteva za povezavo"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, se na vrhu zaslona prikaže ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki ji omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, je na zaslonu prikazana ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string>
<string name="session" msgid="6470628549473641030">"Seja:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-sq/strings.xml b/packages/VpnDialogs/res/values-sq/strings.xml
index 4a96e7b92212..0b4ce4df9514 100644
--- a/packages/VpnDialogs/res/values-sq/strings.xml
+++ b/packages/VpnDialogs/res/values-sq/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kërkesë për lidhje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN-je që e lejon të monitorojë trafikun e rrjetit. Prano vetëm nëse i beson burimit. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; shfaqet në krye të ekranit kur VPN-ja është aktive."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN që i lejon të monitorojë trafikun e rrjetit. Pranoje vetëm nëse i beson burimit. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; shfaqet në ekranin tënd kur është aktive VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-ja është e lidhur"</string>
<string name="session" msgid="6470628549473641030">"Sesioni:"</string>
<string name="duration" msgid="3584782459928719435">"Kohëzgjatja:"</string>
diff --git a/packages/VpnDialogs/res/values-sr/strings.xml b/packages/VpnDialogs/res/values-sr/strings.xml
index 8ce8060e333d..01bd4df700a7 100644
--- a/packages/VpnDialogs/res/values-sr/strings.xml
+++ b/packages/VpnDialogs/res/values-sr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Захтев за повезивање"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која омогућава праћење саобраћаја на мрежи. Прихватите само ако верујете извору. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује у врху екрана када је VPN активан."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Апликација <xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која јој омогућава да прати мрежни саобраћај. Прихватите ово само ако имате поверења у извор. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује на екрану када је VPN активан."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN је повезан"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Трајање:"</string>
diff --git a/packages/VpnDialogs/res/values-sv/strings.xml b/packages/VpnDialogs/res/values-sv/strings.xml
index 16b6a31d7d1a..60ed75250856 100644
--- a/packages/VpnDialogs/res/values-sv/strings.xml
+++ b/packages/VpnDialogs/res/values-sv/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Anslutningsförfrågan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill starta en VPN-anslutning som tillåter att appen övervakar nätverkstrafiken. Godkänn endast detta om du litar på källan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; visas längst upp på skärmen när VPN-anslutningen är aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill skapa en VPN-anslutning så att den kan övervaka nätverkstrafik. Godkänn bara om du litar på källan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; visas på skärmen när VPN-anslutningen är aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN är anslutet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Längd:"</string>
diff --git a/packages/VpnDialogs/res/values-sw/strings.xml b/packages/VpnDialogs/res/values-sw/strings.xml
index ea2688438b7a..c4f46628f8bc 100644
--- a/packages/VpnDialogs/res/values-sw/strings.xml
+++ b/packages/VpnDialogs/res/values-sw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ombi la muunganisho"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> inataka kusanidi muunganisho wa VPN utakaoiruhusu kufuatilia shughuli kwenye mtandao. Kubali ikiwa tu unakiamini chanzo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; huonekana sehemu ya juu ya skrini yako VPN inapofanya kazi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> inagependa kuweka mipangilio ya muunganisho wa VPN inayoiruhusu kufuatilia trafiki ya mtandao. Kubali tu iwapo unaamini chanzo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; huonekana kwenye skrini yako VPN inapotumika."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN imeunganishwa"</string>
<string name="session" msgid="6470628549473641030">"Kipindi:"</string>
<string name="duration" msgid="3584782459928719435">"Muda:"</string>
diff --git a/packages/VpnDialogs/res/values-ta/strings.xml b/packages/VpnDialogs/res/values-ta/strings.xml
index 3b4cc571d860..1385bdc401c3 100644
--- a/packages/VpnDialogs/res/values-ta/strings.xml
+++ b/packages/VpnDialogs/res/values-ta/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"இணைப்புக் கோரிக்கை"</string>
<string name="warning" msgid="809658604548412033">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க வசதியாக VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> கோருகிறது. நம்பகமான மூலத்தை மட்டுமே ஏற்கவும். &lt;br /&gt; &lt;br /&gt; VPN இயக்கத்தில் உள்ளபோது திரையின் மேல் பகுதியில் &lt;img src=vpn_icon /&gt; தோன்றும்."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க அனுமதிக்கும் VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> விரும்புகிறது. நம்பகமான VPN ஆப்ஸாக இருந்தால் மட்டுமே ஏற்கவும். &lt;br /&gt; &lt;br /&gt; VPN இயங்கும்போது உங்கள் திரையில் &lt;img src=vpn_icon /&gt; தோன்றும்."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN இணைக்கப்பட்டது"</string>
<string name="session" msgid="6470628549473641030">"அமர்வு:"</string>
<string name="duration" msgid="3584782459928719435">"காலஅளவு:"</string>
diff --git a/packages/VpnDialogs/res/values-te/strings.xml b/packages/VpnDialogs/res/values-te/strings.xml
index 864c926bc615..2316c62785d9 100644
--- a/packages/VpnDialogs/res/values-te/strings.xml
+++ b/packages/VpnDialogs/res/values-te/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"కనెక్షన్ అభ్యర్థన"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించగలగడానికి VPN కనెక్షన్‌ను సెటప్ చేయాలనుకుంటోంది. మీరు మూలాన్ని విశ్వసిస్తే మాత్రమే ఆమోదించండి. VPN సక్రియంగా ఉన్నప్పుడు మీ స్క్రీన్ ఎగువన &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; కనిపిస్తుంది."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"నెట్‌వర్క్ ట్రాఫిక్‌ను పర్యవేక్షించగలగడానికి, <xliff:g id="APP">%s</xliff:g> VPN కనెక్షన్‌ను సెటప్ చేయాలనుకుంటోంది. మీరు సోర్స్‌ను విశ్వసిస్తే మాత్రమే ఆమోదించండి. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN యాక్టివ్‌గా ఉన్నప్పుడు మీ స్క్రీన్ పై కనిపిస్తుంది."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN కనెక్ట్ చేయబడింది"</string>
<string name="session" msgid="6470628549473641030">"సెషన్:"</string>
<string name="duration" msgid="3584782459928719435">"వ్యవధి:"</string>
diff --git a/packages/VpnDialogs/res/values-th/strings.xml b/packages/VpnDialogs/res/values-th/strings.xml
index 333ff5fefacc..2e174cd7b296 100644
--- a/packages/VpnDialogs/res/values-th/strings.xml
+++ b/packages/VpnDialogs/res/values-th/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ขอการเชื่อมต่อ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการสร้างการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ต้องการตั้งค่าการเชื่อมต่อ VPN เพื่อให้ตรวจสอบการจราจรของข้อมูลในเครือข่ายได้ ยอมรับต่อเมื่อคุณไว้วางใจแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏบนหน้าจอเมื่อใช้งาน VPN อยู่"</string>
<string name="legacy_title" msgid="192936250066580964">"เชื่อมต่อ VPN แล้ว"</string>
<string name="session" msgid="6470628549473641030">"เซสชัน"</string>
<string name="duration" msgid="3584782459928719435">"ระยะเวลา:"</string>
diff --git a/packages/VpnDialogs/res/values-tl/strings.xml b/packages/VpnDialogs/res/values-tl/strings.xml
index 9c01c32d0d0d..ea69fba45f1e 100644
--- a/packages/VpnDialogs/res/values-tl/strings.xml
+++ b/packages/VpnDialogs/res/values-tl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kahilingan sa koneksyon"</string>
<string name="warning" msgid="809658604548412033">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko ng network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa itaas ng iyong screen kapag aktibo ang VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko sa network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa iyong screen kapag aktibo ang VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Nakakonekta ang VPN"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Tagal:"</string>
diff --git a/packages/VpnDialogs/res/values-tr/strings.xml b/packages/VpnDialogs/res/values-tr/strings.xml
index 8665a47e6633..7ffa4bc1e4e9 100644
--- a/packages/VpnDialogs/res/values-tr/strings.xml
+++ b/packages/VpnDialogs/res/values-tr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı isteği"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ağ trafiğini izlemesine olanak veren bir VPN bağlantısı oluşturmak istiyor. Sadece, ilgili kaynağa güveniyorsanız kabul edin. &lt;br /&gt; &lt;br /&gt; VPN aktif olduğunda ekranınızın üst tarafında &lt;img src=vpn_icon /&gt; simgesi görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>, ağ trafiğini izlemesine izin veren bir VPN bağlantısı oluşturmak istiyor. Yalnızca kaynağa güveniyorsanız kabul edin. VPN etkin olduğunda ekranınızda &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN bağlı"</string>
<string name="session" msgid="6470628549473641030">"Oturum:"</string>
<string name="duration" msgid="3584782459928719435">"Süre:"</string>
diff --git a/packages/VpnDialogs/res/values-uk/strings.xml b/packages/VpnDialogs/res/values-uk/strings.xml
index 8f91abf990b3..6411d7cf9d76 100644
--- a/packages/VpnDialogs/res/values-uk/strings.xml
+++ b/packages/VpnDialogs/res/values-uk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запит на під’єднання"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Дозволяйте, якщо довіряєте джерелу. Коли мережа VPN активна, угорі екрана відображається значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Надавайте дозвіл, лише якщо довіряєте джерелу. Коли мережа VPN активна, на екрані з’являється значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Мережу VPN під’єднано"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Тривалість:"</string>
diff --git a/packages/VpnDialogs/res/values-ur/strings.xml b/packages/VpnDialogs/res/values-ur/strings.xml
index db0c2971a64c..3a23e940d9e9 100644
--- a/packages/VpnDialogs/res/values-ur/strings.xml
+++ b/packages/VpnDialogs/res/values-ur/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"کنکشن کی درخواست"</string>
<string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن ترتیب دینا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہے۔ اگر آپ کو ماخذ پر بھروسہ ہے تبھی قبول کریں۔ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; آپ کی اسکرین کے اوپر اس وقت ظاہر ہوتا ہے جب VPN فعال ہوتا ہے۔"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن سیٹ اپ کرنا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہو۔ آپ کو ماخذ پر اعتماد ہونے پر ہی قبول کریں۔ ‎&lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ‏VPN کے فعال ہونے پر آپ کی اسکرین پر ظاہر ہوتا ہے۔"</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN مربوط ہے"</string>
<string name="session" msgid="6470628549473641030">"سیشن:"</string>
<string name="duration" msgid="3584782459928719435">"دورانیہ:"</string>
diff --git a/packages/VpnDialogs/res/values-uz/strings.xml b/packages/VpnDialogs/res/values-uz/strings.xml
index 5a348a0610d3..a3256e7bb927 100644
--- a/packages/VpnDialogs/res/values-uz/strings.xml
+++ b/packages/VpnDialogs/res/values-uz/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ulanish uchun so‘rov"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ilovasi trafikni kuzatish uchun VPN tarmog‘iga ulanmoqchi. Agar ilovaga ishonsangiz, so‘rovga rozi bo‘ling.&lt;br /&gt; &lt;br /&gt;VPN faol bo‘lsa, ekranning yuqori qismida &lt;img src=vpn_icon /&gt; belgisi paydo bo‘ladi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ilovasi tarmoqdagi trafikni kuzatish uchun VPN aloqasini sozlamoqchi. Agar unga ishonsangiz, ruxsat bering. VPN aloqa faolligida ekranda &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; chiqadi."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ulangan"</string>
<string name="session" msgid="6470628549473641030">"Seans:"</string>
<string name="duration" msgid="3584782459928719435">"Davomiyligi:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index 097c9aeee013..184d08d7d665 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. &lt;br /&gt; &lt;br /&gt; Biểu tượng &lt;img src=vpn_icon /&gt; xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Bạn chỉ nên chấp nhận nếu tin tưởng nguồn đó. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sẽ xuất hiện trên màn hình khi VPN đang hoạt động."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
<string name="session" msgid="6470628549473641030">"Phiên"</string>
<string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rCN/strings.xml b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
index 7e528bdfb04a..a7262bedce96 100644
--- a/packages/VpnDialogs/res/values-zh-rCN/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
<string name="warning" msgid="809658604548412033">"“<xliff:g id="APP">%s</xliff:g>”想要设置一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请勿接受此请求。&lt;br /&gt; &lt;br /&gt;启用 VPN 后,屏幕顶部会出现一个 &lt;img src=vpn_icon /&gt; 图标。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"“<xliff:g id="APP">%s</xliff:g>”想要建立一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请不要接受。VPN 处于启用状态时,屏幕上会显示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;。"</string>
<string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
<string name="session" msgid="6470628549473641030">"会话:"</string>
<string name="duration" msgid="3584782459928719435">"时长:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rHK/strings.xml b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
index 49605b08cdee..e4e643234143 100644
--- a/packages/VpnDialogs/res/values-zh-rHK/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。&lt;br /&gt; &lt;br /&gt;VPN 啟用時,畫面頂端會顯示 &lt;img src=vpn_icon /&gt;。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。VPN 啟用時,畫面會顯示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"時段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間︰"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rTW/strings.xml b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
index edd8e61d5555..f54ca4a7a576 100644
--- a/packages/VpnDialogs/res/values-zh-rTW/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線,允許此要求即開放該來源監控網路流量。除非你信任該來源,否則請勿任意接受要求。&lt;br /&gt; &lt;br /&gt;VPN 啟用時,畫面頂端會顯示 &lt;img src=vpn_icon /&gt;。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線,以便監控網路流量。除非你信任該來源,否則請勿接受要求。&lt;br /&gt; &lt;br /&gt; VPN 啟用時,畫面上會顯示 &lt;img src=vpn_icon /&gt;。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"工作階段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間:"</string>
diff --git a/packages/VpnDialogs/res/values-zu/strings.xml b/packages/VpnDialogs/res/values-zu/strings.xml
index 4ab1225e6fc6..c224b13b06da 100644
--- a/packages/VpnDialogs/res/values-zu/strings.xml
+++ b/packages/VpnDialogs/res/values-zu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Isicelo soxhumo"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumo lwe-VPN eyivumela ukwengamela ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ibonakala phezu kwesikrini sakho uma i-VPN isebenza."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"I-<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumano lwe-VPN oluyivumela ukuthi igade ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ivela kusikrini sakho lapho i-VPN isebenza."</string>
<string name="legacy_title" msgid="192936250066580964">"I-VPN ixhunyiwe"</string>
<string name="session" msgid="6470628549473641030">"Iseshini:"</string>
<string name="duration" msgid="3584782459928719435">"Ubude besikhathi:"</string>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 000000000000..adc308600ebb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Lewer programme onder uitsnede-area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 000000000000..648e1d4cf383
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ከተቆረጠው አከባቢ በታች የመተግበሪያዎች ምስልን ስራ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
new file mode 100644
index 000000000000..2d3b506d8cad
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"عرض التطبيقات أسفل منطقة الصورة المقطوعة للشاشة"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
new file mode 100644
index 000000000000..db2b15a142c3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"এপ্‌সমূহ কাটআউট অঞ্চলৰ তলত দেখুৱাওক"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
new file mode 100644
index 000000000000..a6b7c4346e7e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Tətbiqləri kəsilmə sahəsinin aşağısında göstərin"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..f80fa8d14054
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Prikazuj aplikacije ispod oblasti izreza"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
new file mode 100644
index 000000000000..0e5c8bcb55a2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Візуалізацыя праграм ніжэй месца выраза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 000000000000..e97bb57068ca
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Изобразяване на приложенията под областта на прореза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
new file mode 100644
index 000000000000..d13c777fe468
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"কাটআউট এরিয়ার নিচে অ্যাপ রেন্ডার করুন"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 000000000000..9c9f43779b66
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderovanje aplikacija ispod izrezanog područja"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
new file mode 100644
index 000000000000..e0a577e5290f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderitza les aplicacions per sota de l\'àrea de retallada"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 000000000000..0f64473c7260
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykreslovat aplikace pod oblastí výseče"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 000000000000..d0cc43e8025f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gengiv apps under skærmhakkets område"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 000000000000..a7759ea6175a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps unterhalb des Aussparungs-Bereichs darstellen"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 000000000000..b71679a1912f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Απόδοση εφαρμογών κάτω από την περιοχή εγκοπής"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..8b72d9f77c49
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎Render apps below cutout area‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..359cdd0eab52
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps debajo del área de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 000000000000..47f525ec1d28
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicaciones por debajo de la zona de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
new file mode 100644
index 000000000000..0cc5a25868c2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Väljalõikeala all olevate rakenduste renderdamine"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 000000000000..15d7d6045361
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Errendatu mozketa-eremutik kanpo geratzen diren aplikazioak"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
new file mode 100644
index 000000000000..0865f7559ef9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"پرداز زدن برنامه‌ها در زیر ناحیه بریده‌شده"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 000000000000..1a6bf7a16839
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderöi sovellukset lovialueen alle"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..ea0a27b069da
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Rendre les applications sous la zone de découpe"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
new file mode 100644
index 000000000000..6d91a9d603f4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Afficher les applis sous la zone d\'encoche"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 000000000000..382497b1caf0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicacións que aparezan na zona recortada"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
new file mode 100644
index 000000000000..d578d9286d4c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ઍપને કટઆઉટ ક્ષેત્રની નીચે રેન્ડર કરો"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
new file mode 100644
index 000000000000..e1f09f249bda
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ऐप्लिकेशन को कटआउट एरिया के नीचे दिखाएं"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 000000000000..db734e8254e5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderiraj aplikacije ispod područja ureza"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
new file mode 100644
index 000000000000..264095b6f4e2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Alkalmazások megjelenítése a kivágási terület alatt"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
new file mode 100644
index 000000000000..72e67ec1c424
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Արտապատկերել հավելվածները էկրանի կտրված հատվածի ներքևում"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 000000000000..c49bf0ca939a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render aplikasi di bawah area potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
new file mode 100644
index 000000000000..0b90991b0b1e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Birta forrit fyrir neðan útklippta svæðið"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 000000000000..2a0f026b39a6
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visualizza le app sotto l\'area di ritaglio"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
new file mode 100644
index 000000000000..cc7a0a486c54
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"עיבוד האפליקציות שמתחת לאזור המגרעת"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 000000000000..9e99482e5e2a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"カットアウト領域の下でアプリをレンダリング"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 000000000000..5464a5699449
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"აპების ასახვა ჭრილის ქვემოთ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
new file mode 100644
index 000000000000..6a2623f8696f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Экран ойығының астындағы қолданбаларды көрсету"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 000000000000..4b4d169cc738
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"បំប្លែងកម្មវិធីខាងក្រោមផ្នែកឆក"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 000000000000..7a929d13d83f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ಕಟೌಟ್ ಪ್ರದೇಶದ ಕೆಳಗಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ರೆಂಡರ್ ಮಾಡಿ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 000000000000..4b9e64020eee
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"컷아웃 영역 아래에 앱 렌더링"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
new file mode 100644
index 000000000000..1ac6a8bb9c1f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Колдонмолорду кесилген аймактын ылдый жагында көрсөтүү"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 000000000000..4c38580169af
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ສະແດງພາບແອັບຢູ່ທາງລຸ່ມພື້ນທີ່ຮອຍເສັ້ນ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
new file mode 100644
index 000000000000..c43736d006dd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Pateikti programas po išpjovos sritimi"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
new file mode 100644
index 000000000000..f95abb69abf5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Atveidot lietotnes zem izgriezuma apgabala"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 000000000000..ff236be46b14
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Прикажувај апликации под отсечената област"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 000000000000..ef728ab64ab5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"കട്ടൗട്ട് ഭാഗത്തിന് താഴെ ആപ്പുകൾ റെൻഡർ ചെയ്യുക"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 000000000000..23dbe0c822f0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Аппуудыг тасалж авсан хэсгийн доор буулгах"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
new file mode 100644
index 000000000000..42f09cb1a1f0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट क्षेत्राच्या खाली असलेली ॲप्स रेंडर करा"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 000000000000..e348630e0447
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Serahkan apl di bawah kawasan potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 000000000000..90cb0a5f56a4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ဖြတ်ထုတ်ထားသော နေရာအောက်ရှိ အက်ပ်များ ပြသရန်"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
new file mode 100644
index 000000000000..b8b4e7526ab2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gjengi apper under utklippsområdet"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 000000000000..bd213bb64a0d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट गरिएको क्षेत्रभन्दा तल पर्ने एपहरू रेन्डर गर्नुहोस्"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 000000000000..68f5c0701beb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps renderen onder display-cutout"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
new file mode 100644
index 000000000000..162a29e8968c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ଆପଗୁଡ଼ିକୁ କଟଆଉଟ୍ ଏରିଆ ନିମ୍ନରେ ରେଣ୍ଡର୍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 000000000000..908393b1abb0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ਕੱਟਆਊਟ ਖੇਤਰ ਹੇਠ ਐਪਾਂ ਨੂੰ ਰੈਂਡਰ ਕਰੋ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
new file mode 100644
index 000000000000..c027d5266928
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderuj aplikacje pod obszarem wycięcia"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..d09ed97121fa
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..d38ce43204d2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 000000000000..d09ed97121fa
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 000000000000..6e5947c0d753
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redați aplicațiile sub zona de decupaj"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 000000000000..c7f54bbff6a7
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Отображать приложения под вырезом"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 000000000000..4a14a360e857
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"කටවුට් ප්‍රදේශයට පහළින් යෙදුම් විදහන්න"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 000000000000..98b82e636392
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykresľovať aplikácie pod oblasťou výrezu"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
new file mode 100644
index 000000000000..dcf0c842cd36
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Upodobitev aplikacij pod predelom zareze zaslona"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
new file mode 100644
index 000000000000..d7b0676970b8
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Paraqiti aplikacionet poshtë zonës së prerjes"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 000000000000..c2b611e70da9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Приказуј апликације испод области изреза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
new file mode 100644
index 000000000000..3007ffb99e10
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visa appar under skärmutskärningens område"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 000000000000..b605554b46f3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Usionyeshe programu chini ya eneo lenye pengo"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
new file mode 100644
index 000000000000..c4d06fb68564
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"கட் அவுட் பகுதிக்குள்ளாக ஆப்ஸை ரெண்டர் செய்"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
new file mode 100644
index 000000000000..08fa4ae7669b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"కట్అవుట్ ఏరియా కింద యాప్‌లను రెండర్ చేయండి"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 000000000000..9a302507411a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"แสดงผลแอปใต้บริเวณรอยบาก"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 000000000000..a3d4a3afe376
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"I-render ang mga app sa ibaba ng lugar ng cutout"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 000000000000..12e0f3019814
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Uygulamaları kesme alanının altında oluşturun"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 000000000000..08b1521a12a0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Відображати додатки під областю вирізу екрана"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
new file mode 100644
index 000000000000..711b5389b26c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"کٹ آؤٹ ایریا کے نیچے رینڈر ایپس"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 000000000000..7f6f2b45fbd2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Ekran kesimi quyidagi ilovalarni renderlash"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
new file mode 100644
index 000000000000..a7d54fbae9f5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Hiển thị các ứng dụng bên dưới khu vực có vết cắt"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..f596520fad30
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在刘海区域下方呈现应用"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..ddb1df77b00c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在凹口區域下方輸出應用程式"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..7aad79c11bf2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在螢幕凹口底下顯示應用程式畫面"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
new file mode 100644
index 000000000000..d861c5e60708
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Nikezela ngama-app angaphansi kwendawo yokukhipha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 000000000000..b021da75878d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Versteek"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 000000000000..0ca6135776b5
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ደብቅ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
new file mode 100644
index 000000000000..7e41cb1935b9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"إخفاء"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
new file mode 100644
index 000000000000..d2399fffc8c3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকুৱাওক"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
new file mode 100644
index 000000000000..420c5133cbd1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizlədin"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..082e5864065f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
new file mode 100644
index 000000000000..ce75c4584dac
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Схаваць"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 000000000000..7c3170c73355
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скриване"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
new file mode 100644
index 000000000000..1e627256d943
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকান"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 000000000000..082e5864065f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
new file mode 100644
index 000000000000..6ae5ffd32bf8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Amaga"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 000000000000..068bb9463c33
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrýt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 000000000000..6ecb7674d65b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 000000000000..44278e800c5a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ausblenden"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 000000000000..96b1b86dd4bc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Απόκρυψη"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..a20e59446fcb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎Hide‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
new file mode 100644
index 000000000000..4e5428dc59b8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Peida"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 000000000000..f33bb5005cb3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ezkutatu"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
new file mode 100644
index 000000000000..8de756085a84
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"پنهان کردن"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 000000000000..c42e52d1aaaf
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Piilota"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..31fa56766ebc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
new file mode 100644
index 000000000000..31fa56766ebc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
new file mode 100644
index 000000000000..7e4b33a8749c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"છુપાવો"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
new file mode 100644
index 000000000000..1513f2a8ffe5
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"छिपाएं"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 000000000000..082e5864065f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
new file mode 100644
index 000000000000..2b9717fca1cf
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Elrejtés"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
new file mode 100644
index 000000000000..3407bb499645
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Թաքցնել"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 000000000000..6c017b390aa2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
new file mode 100644
index 000000000000..229c113bbdb3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fela"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 000000000000..07444aa372f6
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Nascondi"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
new file mode 100644
index 000000000000..221a129317f9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"הסתרה"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 000000000000..3bf17fb81abe
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"非表示"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 000000000000..160052841a7c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"დამალვა"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
new file mode 100644
index 000000000000..23d02b81c62e
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жасыру"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 000000000000..624b81fa6dcc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"លាក់"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 000000000000..9cd6da7fb314
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ಮರೆಮಾಡಿ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 000000000000..efb656860724
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"숨기기"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
new file mode 100644
index 000000000000..419132513fad
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жашыруу"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 000000000000..8850dfb2d244
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ເຊື່ອງ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
new file mode 100644
index 000000000000..6364b96096e0
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Slėpti"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
new file mode 100644
index 000000000000..61f2ad3d51fb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Paslēpt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 000000000000..505c2059f5ac
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сокриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 000000000000..1c30dec6bc83
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"മറയ്ക്കുക"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 000000000000..7e2719b719fe
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Нуух"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
new file mode 100644
index 000000000000..46f0ab889226
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लपवा"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 000000000000..6c017b390aa2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 000000000000..84be3f9a0986
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ဝှက်ရန်"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
new file mode 100644
index 000000000000..6ecb7674d65b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 000000000000..ff920b290dbb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लुकाइयोस्"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 000000000000..00900f8c6f6f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Verbergen"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
new file mode 100644
index 000000000000..fcfd7252a8bf
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ଲୁଚାନ୍ତୁ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 000000000000..9f37e0bce721
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ਲੁਕਾਓ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
new file mode 100644
index 000000000000..3d65546bf794
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ukryj"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 000000000000..e6281fd09b1a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascundeți"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 000000000000..901839684a9b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скрыть"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 000000000000..b06a52cfc90c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"සඟවන්න"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 000000000000..965d95b0f389
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skryť"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
new file mode 100644
index 000000000000..e8adb9810303
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
new file mode 100644
index 000000000000..85fb95a1f283
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fshih"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 000000000000..26afbf962495
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сакриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
new file mode 100644
index 000000000000..193c1798fc87
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Dölj"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 000000000000..58e35e2eadfd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ficha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
new file mode 100644
index 000000000000..b743c660f183
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"மறை"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
new file mode 100644
index 000000000000..de04152a4e20
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"దాచండి"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 000000000000..1c8bd9d01818
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ซ่อน"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 000000000000..cc45f6335271
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Itago"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 000000000000..b20f1f7d3120
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizle"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 000000000000..938b0e222af1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сховати"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
new file mode 100644
index 000000000000..0f081705f6f1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"چھپائیں"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 000000000000..5d22045d9cdc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Berkitish"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
new file mode 100644
index 000000000000..fc83d257fdba
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ẩn"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..acdfd1c2e4a5
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隐藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..abb8e81fbca4
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..abb8e81fbca4
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
new file mode 100644
index 000000000000..11842d91a53a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fihla"</string>
+</resources>
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlend.java b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
index a1c79ef938c4..3109cd88bd7d 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlend.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
@@ -104,7 +104,7 @@ public class ScriptIntrinsicBlend extends ScriptIntrinsic {
* @param opt LaunchOptions for clipping
*/
public void forEachSrc(Allocation ain, Allocation aout, Script.LaunchOptions opt) {
- blend(1, ain, aout, null);
+ blend(1, ain, aout, opt);
}
/**
@@ -641,4 +641,3 @@ public class ScriptIntrinsicBlend extends ScriptIntrinsic {
}
*/
}
-
diff --git a/services/Android.bp b/services/Android.bp
index c83a697a71c1..a2d6beb41d29 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -21,6 +21,9 @@ java_defaults {
// "-XepPatchLocation:/tmp/refaster/",
],
},
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ },
}
filegroup {
@@ -130,6 +133,7 @@ java_library {
libs: [
"android.hidl.manager-V1.0-java",
"framework-tethering.stubs.module_lib",
+ "service-art.stubs.system_server",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/OWNERS b/services/OWNERS
index 3b972e922e95..b7128a32fcee 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,6 +1,6 @@
per-file Android.bp = file:platform/build/soong:/OWNERS
# art-team@ manages the system server profile
-per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
per-file java/com/android/server/* = toddke@google.com,patb@google.com
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index e9c9899023b1..eec875a6d95e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -21,8 +21,13 @@ import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILIT
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
+import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
@@ -31,6 +36,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
@@ -103,10 +109,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
FingerprintGestureDispatcher.FingerprintGestureClient {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CONNECTION =
- LOG_TAG + ".IAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CLIENT =
- LOG_TAG + ".IAccessibilityServiceClient";
+ private static final String TRACE_SVC_CONN = LOG_TAG + ".IAccessibilityServiceConnection";
+ private static final String TRACE_SVC_CLIENT = LOG_TAG + ".IAccessibilityServiceClient";
+ private static final String TRACE_WM = "WindowManagerInternal";
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
protected static final String TAKE_SCREENSHOT = "takeScreenshot";
@@ -134,6 +139,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected final AccessibilitySecurityPolicy mSecurityPolicy;
protected final AccessibilityTrace mTrace;
+ // The attribution tag set by the service that is bound to this instance
+ protected String mAttributionTag;
+
// The service that's bound to this instance. Whenever this value is non-null, this
// object is registered as a death recipient
IBinder mService;
@@ -298,9 +306,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onKeyEvent",
- keyEvent + ", " + sequenceNumber);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber);
}
mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
} catch (RemoteException e) {
@@ -365,17 +372,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setOnKeyEventResult",
- "handled=" + handled + ";sequence=" + sequence);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setOnKeyEventResult", "handled=" + handled + ";sequence=" + sequence);
}
mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
}
@Override
public AccessibilityServiceInfo getServiceInfo() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getServiceInfo");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getServiceInfo", "");
}
synchronized (mLock) {
return mAccessibilityServiceInfo;
@@ -393,8 +399,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setServiceInfo", "info=" + info);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setServiceInfo", "info=" + info);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -416,13 +422,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
+ @Override
+ public void setAttributionTag(String attributionTag) {
+ mAttributionTag = attributionTag;
+ }
+
+ String getAttributionTag() {
+ return mAttributionTag;
+ }
+
protected abstract boolean hasRightsToCurrentUserLocked();
@Nullable
@Override
public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindows");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindows", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -458,8 +473,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindow", "windowId=" + windowId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindow", "windowId=" + windowId);
}
synchronized (mLock) {
int displayId = Display.INVALID_DISPLAY;
@@ -496,8 +511,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByViewId",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfosByViewId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId="
+ interactionId + ";callback=" + callback + ";interrogatingTid="
@@ -539,6 +554,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfosByViewId",
+ accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -564,8 +585,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByText",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfosByText",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId
+ ";callback=" + callback + ";interrogatingTid=" + interrogatingTid);
@@ -606,6 +627,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfosByText",
+ accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -631,13 +658,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid, Bundle arguments) throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfoByAccessibilityId",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfoByAccessibilityId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
- + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
- + ";arguments=" + arguments);
+ + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
+ + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
+ + ";arguments=" + arguments);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -675,6 +701,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId",
+ accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";"
+ + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";"
+ + interrogatingTid + ";" + spec + ";" + arguments);
+ }
try {
connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
@@ -700,12 +732,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findFocus",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findFocus",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
- + interactionId + ";callback=" + callback + ";interrogatingTid="
- + interrogatingTid);
+ + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -743,6 +775,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findFocus",
+ accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findFocus(accessibilityNodeId, focusType,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -768,12 +806,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".focusSearch",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("focusSearch",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";direction=" + direction + ";interactionId="
- + interactionId + ";callback=" + callback + ";interrogatingTid="
- + interrogatingTid);
+ + accessibilityNodeId + ";direction=" + direction + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -810,6 +848,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("focusSearch",
+ accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion
+ + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";"
+ + interrogatingPid + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().focusSearch(accessibilityNodeId, direction,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -832,17 +876,17 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".sendGesture",
- "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn(
+ "sendGesture", "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
}
}
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".dispatchGesture", "sequence="
- + sequence + ";gestureSteps=" + gestureSteps + ";displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("dispatchGesture", "sequence=" + sequence + ";gestureSteps="
+ + gestureSteps + ";displayId=" + displayId);
}
}
@@ -851,12 +895,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performAccessibilityAction",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("performAccessibilityAction",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
- + ";interactionId=" + interactionId + ";callback=" + callback
- + ";interrogatingTid=" + interrogatingTid);
+ + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
+ + ";interactionId=" + interactionId + ";callback=" + callback
+ + ";interrogatingTid=" + interrogatingTid);
}
final int resolvedWindowId;
synchronized (mLock) {
@@ -879,9 +923,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean performGlobalAction(int action) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performGlobalAction",
- "action=" + action);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("performGlobalAction", "action=" + action);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -893,8 +936,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSystemActions");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getSystemActions", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -906,9 +949,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean isFingerprintGestureDetectionAvailable() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CONNECTION + ".isFingerprintGestureDetectionAvailable");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("isFingerprintGestureDetectionAvailable", "");
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return false;
@@ -923,9 +965,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationScale(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationScale",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationScale", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -942,9 +983,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public Region getMagnificationRegion(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationRegion",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationRegion", "displayId=" + displayId);
}
synchronized (mLock) {
final Region region = Region.obtain();
@@ -970,9 +1010,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationCenterX(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterX",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationCenterX", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -996,9 +1035,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationCenterY(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterY",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationCenterY", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1032,9 +1070,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean resetMagnification(int displayId, boolean animate) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".resetMagnification",
- "displayId=" + displayId + ";animate=" + animate);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("resetMagnification", "displayId=" + displayId + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1058,10 +1095,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
float centerY, boolean animate) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationScaleAndCenter",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setMagnificationScaleAndCenter",
"displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
- + ";centerY=" + centerY + ";animate=" + animate);
+ + ";centerY=" + centerY + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1087,8 +1124,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationCallbackEnabled",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setMagnificationCallbackEnabled",
"displayId=" + displayId + ";enabled=" + enabled);
}
mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
@@ -1100,18 +1137,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setSoftKeyboardCallbackEnabled(boolean enabled) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardCallbackEnabled",
- "enabled=" + enabled);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setSoftKeyboardCallbackEnabled", "enabled=" + enabled);
}
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
@Override
public void takeScreenshot(int displayId, RemoteCallback callback) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".takeScreenshot",
- "displayId=" + displayId + ";callback=" + callback);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("takeScreenshot", "displayId=" + displayId + ";callback=" + callback);
}
final long currentTimestamp = SystemClock.uptimeMillis();
if (mRequestTakeScreenshotTimestampMs != 0
@@ -1237,6 +1272,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
+ if (wmTracingEnabled()) {
+ logTraceWM("addWindowToken",
+ overlayWindowToken + ";TYPE_ACCESSIBILITY_OVERLAY;" + displayId + ";null");
+ }
mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
displayId, null /* options */);
synchronized (mLock) {
@@ -1263,6 +1302,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
public void onDisplayRemoved(int displayId) {
final long identity = Binder.clearCallingIdentity();
+ if (wmTracingEnabled()) {
+ logTraceWM(
+ "addWindowToken", mOverlayWindowTokens.get(displayId) + ";true;" + displayId);
+ }
try {
mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true,
displayId);
@@ -1282,9 +1325,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
@Override
public IBinder getOverlayWindowToken(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getOverlayWindowToken",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getOverlayWindowToken", "displayId=" + displayId);
}
synchronized (mLock) {
return mOverlayWindowTokens.get(displayId);
@@ -1299,9 +1341,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
@Override
public int getWindowIdForLeashToken(@NonNull IBinder token) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindowIdForLeashToken",
- "token=" + token);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindowIdForLeashToken", "token=" + token);
}
synchronized (mLock) {
return mA11yWindowManager.getWindowIdLocked(token);
@@ -1314,8 +1355,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
// Clear the proxy in the other process so this
// IAccessibilityServiceConnection can be garbage collected.
if (mServiceInterface != null) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", "null, " + mId + ", null");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("init", "null, " + mId + ", null");
}
mServiceInterface.init(null, mId, null);
}
@@ -1465,9 +1506,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityEvent",
- event + ";" + serviceWantsEvent);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent);
}
listener.onAccessibilityEvent(event, serviceWantsEvent);
if (DEBUG) {
@@ -1522,9 +1562,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onMagnificationChanged", displayId
- + ", " + region + ", " + scale + ", " + centerX + ", " + centerY);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
+ + scale + ", " + centerX + ", " + centerY);
}
listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
} catch (RemoteException re) {
@@ -1541,9 +1581,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSoftKeyboardShowModeChanged",
- String.valueOf(showState));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState));
}
listener.onSoftKeyboardShowModeChanged(showState);
} catch (RemoteException re) {
@@ -1557,9 +1596,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonClicked",
- String.valueOf(displayId));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId));
}
listener.onAccessibilityButtonClicked(displayId);
} catch (RemoteException re) {
@@ -1579,9 +1617,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonAvailabilityChanged",
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityButtonAvailabilityChanged",
String.valueOf(available));
}
listener.onAccessibilityButtonAvailabilityChanged(available);
@@ -1597,9 +1634,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onGesture",
- gestureInfo.toString());
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onGesture", gestureInfo.toString());
}
listener.onGesture(gestureInfo);
} catch (RemoteException re) {
@@ -1613,8 +1649,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSystemActionsChanged");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onSystemActionsChanged", "");
}
listener.onSystemActionsChanged();
} catch (RemoteException re) {
@@ -1628,8 +1664,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".clearAccessibilityCache");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("clearAccessibilityCache", "");
}
listener.clearAccessibilityCache();
} catch (RemoteException re) {
@@ -1747,6 +1783,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
LocalServices.getService(ActivityTaskManagerInternal.class)
.setFocusedActivity(activityToken);
}
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("performAccessibilityAction",
+ accessibilityNodeId + ";" + action + ";" + arguments + ";" + interactionId
+ + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + ";"
+ + interrogatingTid);
+ }
connection.getRemote().performAccessibilityAction(accessibilityNodeId, action,
arguments, interactionId, callback, fetchFlags, interrogatingPid,
interrogatingTid);
@@ -1957,8 +1999,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setGestureDetectionPassthroughRegion",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setGestureDetectionPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
@@ -1966,8 +2008,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setTouchExplorationPassthroughRegion",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setTouchExplorationPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
@@ -1975,20 +2017,56 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setFocusAppearance(int strokeWidth, int color) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setFocusAppearance",
- "strokeWidth=" + strokeWidth + ";color=" + color);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setFocusAppearance", "strokeWidth=" + strokeWidth + ";color=" + color);
}
}
@Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, Bundle callingStack) {
- if (mTrace.isA11yTracingEnabled()) {
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, Bundle callingStack) {
+ if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) {
ArrayList<StackTraceElement> list =
(ArrayList<StackTraceElement>) callingStack.getSerializable(CALL_STACK);
- mTrace.logTrace(timestamp, where, callingParams, processId, threadId, callingUid,
- list.toArray(new StackTraceElement[list.size()]));
+ HashSet<String> ignoreList =
+ (HashSet<String>) callingStack.getSerializable(IGNORE_CALL_STACK);
+ mTrace.logTrace(timestamp, where, loggingTypes, callingParams, processId, threadId,
+ callingUid, list.toArray(new StackTraceElement[list.size()]), ignoreList);
}
}
+
+ protected boolean svcClientTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
+ }
+
+ protected void logTraceSvcClient(String methodName, String params) {
+ mTrace.logTrace(TRACE_SVC_CLIENT + "." + methodName,
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, params);
+ }
+
+ protected boolean svcConnTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CONNECTION);
+ }
+
+ protected void logTraceSvcConn(String methodName, String params) {
+ mTrace.logTrace(TRACE_SVC_CONN + "." + methodName,
+ FLAGS_ACCESSIBILITY_SERVICE_CONNECTION, params);
+ }
+
+ protected boolean intConnTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
+ protected void logTraceIntConn(String methodName, String params) {
+ mTrace.logTrace(LOG_TAG + "." + methodName,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION, params);
+ }
+
+ protected boolean wmTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ protected void logTraceWM(String methodName, String params) {
+ mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 7403af7605bc..7d2b71f7852b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,7 +19,9 @@ package com.android.server.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.os.PowerManager;
@@ -43,7 +45,10 @@ import com.android.server.accessibility.magnification.WindowMagnificationGesture
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.StringJoiner;
/**
* This class is an input filter for implementing accessibility features such
@@ -171,9 +176,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private int mEnabledFeatures;
- private EventStreamState mMouseStreamState;
+ private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);
- private EventStreamState mTouchScreenStreamState;
+ private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);
private EventStreamState mKeyboardStreamState;
@@ -211,10 +216,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
super.onUninstalled();
}
- void onDisplayChanged() {
+ void onDisplayAdded(@NonNull Display display) {
if (mInstalled) {
- disableFeatures();
- enableFeatures();
+ resetStreamStateForDisplay(display.getDisplayId());
+ enableFeaturesForDisplay(display);
+ }
+ }
+
+ void onDisplayRemoved(int displayId) {
+ if (mInstalled) {
+ disableFeaturesForDisplay(displayId);
+ resetStreamStateForDisplay(displayId);
}
}
@@ -224,7 +236,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
-
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mAms.getTraceManager().logTrace(TAG + ".onInputEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";policyFlags=" + policyFlags);
+ }
if (mEventHandler.size() == 0) {
if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
super.onInputEvent(event, policyFlags);
@@ -237,16 +254,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return;
}
- int eventSource = event.getSource();
+ final int eventSource = event.getSource();
+ final int displayId = event.getDisplayId();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
state.reset();
- clearEventsForAllEventHandlers(eventSource);
+ clearEventStreamHandler(displayId, eventSource);
super.onInputEvent(event, policyFlags);
return;
}
if (state.updateInputSource(event.getSource())) {
- clearEventsForAllEventHandlers(eventSource);
+ clearEventStreamHandler(displayId, eventSource);
}
if (!state.inputSourceValid()) {
@@ -275,35 +293,39 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*/
private EventStreamState getEventStreamState(InputEvent event) {
if (event instanceof MotionEvent) {
- if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
- if (mTouchScreenStreamState == null) {
- mTouchScreenStreamState = new TouchScreenEventStreamState();
- }
- return mTouchScreenStreamState;
- }
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
- if (mMouseStreamState == null) {
- mMouseStreamState = new MouseEventStreamState();
- }
- return mMouseStreamState;
- }
+ final int displayId = event.getDisplayId();
+
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+ if (touchScreenStreamState == null) {
+ touchScreenStreamState = new TouchScreenEventStreamState();
+ mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
+ }
+ return touchScreenStreamState;
+ }
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+ if (mouseStreamState == null) {
+ mouseStreamState = new MouseEventStreamState();
+ mMouseStreamStates.put(displayId, mouseStreamState);
+ }
+ return mouseStreamState;
+ }
} else if (event instanceof KeyEvent) {
- if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
- if (mKeyboardStreamState == null) {
- mKeyboardStreamState = new KeyboardEventStreamState();
- }
- return mKeyboardStreamState;
- }
+ if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
+ if (mKeyboardStreamState == null) {
+ mKeyboardStreamState = new KeyboardEventStreamState();
+ }
+ return mKeyboardStreamState;
+ }
}
return null;
}
- private void clearEventsForAllEventHandlers(int eventSource) {
- for (int i = 0; i < mEventHandler.size(); i++) {
- final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
- if (eventHandler != null) {
- eventHandler.clearEvents(eventSource);
- }
+ private void clearEventStreamHandler(int displayId, int eventSource) {
+ final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
+ if (eventHandler != null) {
+ eventHandler.clearEvents(eventSource);
}
}
@@ -419,55 +441,69 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private void enableFeatures() {
if (DEBUG) Slog.i(TAG, "enableFeatures()");
- resetStreamState();
+ resetAllStreamState();
final ArrayList<Display> displaysList = mAms.getValidDisplayList();
- if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
- mAutoclickController = new AutoclickController(mContext, mUserId);
- addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ enableFeaturesForDisplay(displaysList.get(i));
}
+ enableDisplayIndependentFeatures();
+ }
- for (int i = displaysList.size() - 1; i >= 0; i--) {
- final int displayId = displaysList.get(i).getDisplayId();
- final Context displayContext = mContext.createDisplayContext(displaysList.get(i));
+ private void enableFeaturesForDisplay(Display display) {
+ if (DEBUG) {
+ Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
+ }
- if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
- TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
- if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
- explorer.setServiceHandlesDoubleTap(true);
- }
- if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
- explorer.setMultiFingerGesturesEnabled(true);
- }
- if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
- explorer.setTwoFingerPassthroughEnabled(true);
- }
- if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
- explorer.setSendMotionEventsEnabled(true);
- }
- addFirstEventHandler(displayId, explorer);
- mTouchExplorer.put(displayId, explorer);
- }
+ final Context displayContext = mContext.createDisplayContext(display);
+ final int displayId = display.getDisplayId();
- if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
- || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
- final MagnificationGestureHandler magnificationGestureHandler =
- createMagnificationGestureHandler(displayId,
- displayContext);
- addFirstEventHandler(displayId, magnificationGestureHandler);
- mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+ if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
+ if (mAutoclickController == null) {
+ mAutoclickController = new AutoclickController(
+ mContext, mUserId, mAms.getTraceManager());
}
+ addFirstEventHandler(displayId, mAutoclickController);
+ }
- if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
- MotionEventInjector injector = new MotionEventInjector(
- mContext.getMainLooper());
- addFirstEventHandler(displayId, injector);
- mMotionEventInjectors.put(displayId, injector);
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
+ if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
+ explorer.setServiceHandlesDoubleTap(true);
+ }
+ if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
+ explorer.setMultiFingerGesturesEnabled(true);
+ }
+ if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
+ explorer.setTwoFingerPassthroughEnabled(true);
}
+ if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
+ explorer.setSendMotionEventsEnabled(true);
+ }
+ addFirstEventHandler(displayId, explorer);
+ mTouchExplorer.put(displayId, explorer);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+ || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
+ final MagnificationGestureHandler magnificationGestureHandler =
+ createMagnificationGestureHandler(displayId,
+ displayContext);
+ addFirstEventHandler(displayId, magnificationGestureHandler);
+ mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+ MotionEventInjector injector = new MotionEventInjector(
+ mContext.getMainLooper(), mAms.getTraceManager());
+ addFirstEventHandler(displayId, injector);
+ mMotionEventInjectors.put(displayId, injector);
}
+ }
+ private void enableDisplayIndependentFeatures() {
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
mAms.setMotionEventInjectors(mMotionEventInjectors);
}
@@ -500,55 +536,57 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mEventHandler.put(displayId, eventHandler);
}
- /**
- * Adds an event handler to the event handler chain for all displays. The handler is added at
- * the beginning of the chain.
- *
- * @param displayList The list of displays
- * @param handler The handler to be added to the event handlers list.
- */
- private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
- EventStreamTransformation handler) {
- for (int i = 0; i < displayList.size(); i++) {
- final int displayId = displayList.get(i).getDisplayId();
- addFirstEventHandler(displayId, handler);
+ private void disableFeatures() {
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
}
+ mAms.setMotionEventInjectors(null);
+ disableDisplayIndependentFeatures();
+
+ resetAllStreamState();
}
- private void disableFeatures() {
- for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
- final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
- if (injector != null) {
- injector.onDestroy();
- }
+ private void disableFeaturesForDisplay(int displayId) {
+ if (DEBUG) {
+ Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
}
- mAms.setMotionEventInjectors(null);
- mMotionEventInjectors.clear();
+
+ final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
+ if (injector != null) {
+ injector.onDestroy();
+ mMotionEventInjectors.remove(displayId);
+ }
+
+ final TouchExplorer explorer = mTouchExplorer.get(displayId);
+ if (explorer != null) {
+ explorer.onDestroy();
+ mTouchExplorer.remove(displayId);
+ }
+
+ final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
+ if (handler != null) {
+ handler.onDestroy();
+ mMagnificationGestureHandler.remove(displayId);
+ }
+
+ final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
+ if (eventStreamTransformation != null) {
+ mEventHandler.remove(displayId);
+ }
+ }
+
+ private void disableDisplayIndependentFeatures() {
if (mAutoclickController != null) {
mAutoclickController.onDestroy();
mAutoclickController = null;
}
- for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
- final TouchExplorer explorer = mTouchExplorer.valueAt(i);
- if (explorer != null) {
- explorer.onDestroy();
- }
- }
- mTouchExplorer.clear();
- for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
- final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);
- if (handler != null) {
- handler.onDestroy();
- }
- }
- mMagnificationGestureHandler.clear();
+
if (mKeyboardInterceptor != null) {
mKeyboardInterceptor.onDestroy();
mKeyboardInterceptor = null;
}
-
- mEventHandler.clear();
- resetStreamState();
}
private MagnificationGestureHandler createMagnificationGestureHandler(
@@ -563,32 +601,46 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
final Context uiContext = displayContext.createWindowContext(
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
- mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(),
- detectControlGestures, triggerable,
+ mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+ mAms.getMagnificationController(), detectControlGestures, triggerable,
displayId);
} else {
final Context uiContext = displayContext.createWindowContext(
TYPE_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
- mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(),
- detectControlGestures, triggerable,
+ mAms.getFullScreenMagnificationController(), mAms.getTraceManager(),
+ mAms.getMagnificationController(), detectControlGestures, triggerable,
new WindowMagnificationPromptController(displayContext, mUserId), displayId);
}
return magnificationGestureHandler;
}
- void resetStreamState() {
- if (mTouchScreenStreamState != null) {
- mTouchScreenStreamState.reset();
- }
- if (mMouseStreamState != null) {
- mMouseStreamState.reset();
+ void resetAllStreamState() {
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
}
+
if (mKeyboardStreamState != null) {
mKeyboardStreamState.reset();
}
}
+ void resetStreamStateForDisplay(int displayId) {
+ final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+ if (touchScreenStreamState != null) {
+ touchScreenStreamState.reset();
+ mTouchScreenStreamStates.remove(displayId);
+ }
+
+ final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+ if (mouseStreamState != null) {
+ mouseStreamState.reset();
+ mMouseStreamStates.remove(displayId);
+ }
+ }
+
@Override
public void onDestroy() {
/* ignore */
@@ -839,4 +891,45 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
}
}
+
+ /**
+ * Dumps all {@link AccessibilityInputFilter}s here.
+ */
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (mEventHandler == null) {
+ return;
+ }
+ pw.append("A11yInputFilter Info : ");
+ pw.println();
+
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+ for (int i = 0; i < displaysList.size(); i++) {
+ final int displayId = displaysList.get(i).getDisplayId();
+ EventStreamTransformation next = mEventHandler.get(displayId);
+ if (next != null) {
+ pw.append("Enabled features of Display [");
+ pw.append(Integer.toString(displayId));
+ pw.append("] = ");
+
+ final StringJoiner joiner = new StringJoiner(",", "[", "]");
+
+ while (next != null) {
+ if (next instanceof MagnificationGestureHandler) {
+ joiner.add("MagnificationGesture");
+ } else if (next instanceof KeyboardInterceptor) {
+ joiner.add("KeyboardInterceptor");
+ } else if (next instanceof TouchExplorer) {
+ joiner.add("TouchExplorer");
+ } else if (next instanceof AutoclickController) {
+ joiner.add("AutoclickController");
+ } else if (next instanceof MotionEventInjector) {
+ joiner.add("MotionEventInjector");
+ }
+ next = next.getNext();
+ }
+ pw.append(joiner.toString());
+ }
+ pw.println();
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f63198866b08..7b2b8e4e50e0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,15 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_FINGERPRINT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -289,8 +298,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mTraceManager = new AccessibilityTraceManager(
- mWindowManagerService.getAccessibilityController(), this);
+ mTraceManager = AccessibilityTraceManager.getInstance(
+ mWindowManagerService.getAccessibilityController(), this, mLock);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = packageManager;
@@ -311,8 +320,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mTraceManager = new AccessibilityTraceManager(
- mWindowManagerService.getAccessibilityController(), this);
+ mTraceManager = AccessibilityTraceManager.getInstance(
+ mWindowManagerService.getAccessibilityController(), this, mLock);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -324,7 +333,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
this);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
- mWindowManagerService, this, mSecurityPolicy, this);
+ mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
mMagnificationController = new MagnificationController(this, mLock, mContext);
init();
@@ -339,26 +348,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public int getCurrentUserIdLocked() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getCurrentUserIdLocked");
- }
return mCurrentUserId;
}
@Override
public boolean isAccessibilityButtonShown() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".isAccessibilityButtonShown");
- }
return mIsAccessibilityButtonShown;
}
@Override
public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(
- LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
- }
mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
userState.mBoundServices);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -424,8 +423,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER);
}
synchronized (mLock) {
@@ -452,8 +452,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// mBindingServices in binderDied() during updating. Remove services from this
// package from mBindingServices, and then update the user state to re-bind new
// versions of them.
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
synchronized (mLock) {
@@ -485,8 +486,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
@@ -529,8 +531,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"intent=" + intent + ";packages=" + packages + ";uid=" + uid
+ ";doit=" + doit);
}
@@ -580,8 +583,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".BR.onReceive",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) {
+ mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER,
"context=" + context + ";intent=" + intent);
}
@@ -668,8 +671,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".addClient",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".addClient", FLAGS_ACCESSIBILITY_MANAGER,
"callback=" + callback + ";userId=" + userId);
}
@@ -739,11 +742,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", FLAGS_ACCESSIBILITY_MANAGER,
"event=" + event + ";userId=" + userId);
}
boolean dispatchEvent = false;
+ int resolvedUserId;
synchronized (mLock) {
if (event.getWindowId() ==
@@ -759,8 +763,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ resolvedUserId = mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// Make sure the reported package is one the caller has access to.
event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked(
@@ -792,17 +795,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
int displayId = Display.INVALID_DISPLAY;
synchronized (mLock) {
final int windowId = event.getWindowId();
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- && windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
- mCurrentUserId, windowId);
+ resolvedUserId, windowId);
+ event.setDisplayId(displayId);
}
- if (displayId != Display.INVALID_DISPLAY
+
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ && displayId != Display.INVALID_DISPLAY
&& mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
shouldComputeWindows = true;
}
}
if (shouldComputeWindows) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.computeWindowsForAccessibility",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "display=" + displayId);
+ }
final WindowManagerInternal wm = LocalServices.getService(
WindowManagerInternal.class);
wm.computeWindowsForAccessibility(displayId);
@@ -835,9 +844,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void registerSystemAction(RemoteAction action, int actionId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".registerSystemAction",
- "action=" + action + ";actionId=" + actionId);
+ FLAGS_ACCESSIBILITY_MANAGER, "action=" + action + ";actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().registerSystemAction(actionId, action);
@@ -850,8 +859,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void unregisterSystemAction(int actionId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction",
+ FLAGS_ACCESSIBILITY_MANAGER, "actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().unregisterSystemAction(actionId);
@@ -867,9 +877,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList",
- "userId=" + userId);
+ FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
}
final int resolvedUserId;
@@ -903,8 +913,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
+ FLAGS_ACCESSIBILITY_MANAGER,
"feedbackType=" + feedbackType + ";userId=" + userId);
}
@@ -936,8 +947,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void interrupt(int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".interrupt",
+ FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
}
List<IAccessibilityServiceClient> interfacesToInterrupt;
@@ -966,8 +978,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
}
interfacesToInterrupt.get(i).onInterrupt();
} catch (RemoteException re) {
@@ -981,8 +995,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
IAccessibilityInteractionConnection connection, String packageName,
int userId) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
+ FLAGS_ACCESSIBILITY_MANAGER,
"windowToken=" + windowToken + "leashToken=" + leashToken + ";connection="
+ connection + "; packageName=" + packageName + ";userId=" + userId);
}
@@ -993,9 +1008,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection",
- "window=" + window);
+ FLAGS_ACCESSIBILITY_MANAGER, "window=" + window);
}
mA11yWindowManager.removeAccessibilityInteractionConnection(window);
}
@@ -1003,9 +1018,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setPictureInPictureActionReplacingConnection(
IAccessibilityInteractionConnection connection) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
- "connection=" + connection);
+ FLAGS_ACCESSIBILITY_MANAGER, "connection=" + connection);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
SET_PIP_ACTION_REPLACEMENT);
@@ -1017,10 +1032,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
int flags) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
- + ";serviceClient=" + serviceClient + ";accessibilityServiceInfo="
- + accessibilityServiceInfo + ";flags=" + flags);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService",
+ FLAGS_ACCESSIBILITY_MANAGER,
+ "owner=" + owner + ";serviceClient=" + serviceClient
+ + ";accessibilityServiceInfo=" + accessibilityServiceInfo + ";flags=" + flags);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
@@ -1037,9 +1053,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
- "serviceClient=" + serviceClient);
+ FLAGS_ACCESSIBILITY_MANAGER, "serviceClient=" + serviceClient);
}
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
@@ -1049,15 +1065,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
ComponentName service, boolean touchExplorationEnabled) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(
LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
+ FLAGS_ACCESSIBILITY_MANAGER,
"service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled);
}
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.isKeyguardLocked",
+ FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
if (!mWindowManagerService.isKeyguardLocked()) {
return;
}
@@ -1083,9 +1104,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public IBinder getWindowToken(int windowId, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getWindowToken",
- "windowId=" + windowId + ";userId=" + userId);
+ FLAGS_ACCESSIBILITY_MANAGER, "windowId=" + windowId + ";userId=" + userId);
}
mSecurityPolicy.enforceCallingPermission(
@@ -1127,8 +1148,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
+ FLAGS_ACCESSIBILITY_MANAGER,
"displayId=" + displayId + ";targetName=" + targetName);
}
@@ -1157,9 +1179,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged",
- "shown=" + shown);
+ FLAGS_ACCESSIBILITY_MANAGER, "shown=" + shown);
}
mSecurityPolicy.enforceCallingOrSelfPermission(
@@ -1190,10 +1212,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void onSystemActionsChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged");
- }
-
synchronized (mLock) {
AccessibilityUserState state = getCurrentUserStateLocked();
notifySystemActionsChangedLocked(state);
@@ -1256,11 +1274,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked",
- "displayId=" + displayId);
- }
-
final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
MotionEventInjector motionEventInjector = null;
while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) {
@@ -1323,6 +1336,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
token = getWindowToken(windowId, mCurrentUserId);
}
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.getWindowFrame",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "token=" + token + ";outBounds=" + outBounds);
+ }
mWindowManagerService.getWindowFrame(token, outBounds);
if (!outBounds.isEmpty()) {
return true;
@@ -1471,7 +1488,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private int getClientStateLocked(AccessibilityUserState userState) {
return userState.getClientStateLocked(
mUiAutomationManager.isUiAutomationRunningLocked(),
- mTraceManager.isA11yTracingEnabled());
+ mTraceManager.getTraceStateForAccessibilityManagerClientState());
}
private InteractionBridge getInteractionBridge() {
@@ -1681,6 +1698,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void updateRelevantEventsLocked(AccessibilityUserState userState) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateRelevantEventsLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes;
@@ -1830,12 +1851,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked",
- "settingName=" + settingName + ";componentNames=" + componentNames + ";userId="
- + userId);
- }
-
persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
componentName -> componentName.flattenToShortString());
}
@@ -1960,7 +1975,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
final int clientState = getClientStateLocked(userState);
if (userState.getLastSentClientStateLocked() != clientState
&& (mGlobalClients.getRegisteredCallbackCount() > 0
@@ -1983,6 +1998,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void sendStateToClients(int clientState,
RemoteCallbackList<IAccessibilityManagerClient> clients) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
+ FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
+ }
clients.broadcast(ignoreRemoteException(
client -> client.setState(clientState)));
}
@@ -2003,6 +2022,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void notifyClientsOfServicesStateChange(
RemoteCallbackList<IAccessibilityManagerClient> clients, long uiTimeout) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
+ FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout);
+ }
clients.broadcast(ignoreRemoteException(
client -> client.notifyServicesStateChanged(uiTimeout)));
}
@@ -2082,6 +2105,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
if (setInputFilter) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL
+ | FLAGS_INPUT_FILTER)) {
+ mTraceManager.logTrace("WindowManagerInternal.setInputFilter",
+ FLAGS_WINDOW_MANAGER_INTERNAL | FLAGS_INPUT_FILTER,
+ "inputFilter=" + inputFilter);
+ }
mWindowManagerService.setInputFilter(inputFilter);
}
}
@@ -2805,26 +2834,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@GuardedBy("mLock")
@Override
public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked",
- "windowId=" + windowId);
- }
-
IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
mCurrentUserId, windowId);
if (windowToken != null) {
- return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
- windowToken);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken);
+ }
+
+ return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken);
}
return null;
}
@Override
public KeyEventDispatcher getKeyEventDispatcher() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getKeyEventDispatcher");
- }
-
if (mKeyEventDispatcher == null) {
mKeyEventDispatcher = new KeyEventDispatcher(
mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock,
@@ -2837,13 +2861,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getPendingIntentActivity",
- "context=" + context + ";requestCode=" + requestCode + ";intent=" + intent
- + ";flags=" + flags);
- }
-
-
return PendingIntent.getActivity(context, requestCode, intent, flags);
}
@@ -2858,9 +2875,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void performAccessibilityShortcut(String targetName) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
- "targetName=" + targetName);
+ FLAGS_ACCESSIBILITY_MANAGER, "targetName=" + targetName);
}
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
@@ -3048,9 +3065,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets",
- "shortcutType=" + shortcutType);
+ FLAGS_ACCESSIBILITY_MANAGER, "shortcutType=" + shortcutType);
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@@ -3122,11 +3139,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked",
- "event=" + event);
- }
-
sendAccessibilityEventLocked(event, mCurrentUserId);
}
@@ -3148,8 +3160,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public boolean sendFingerprintGesture(int gestureKeyCode) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT)) {
mTraceManager.logTrace(LOG_TAG + ".sendFingerprintGesture",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT,
"gestureKeyCode=" + gestureKeyCode);
}
@@ -3174,9 +3188,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getAccessibilityWindowId",
- "windowToken=" + windowToken);
+ FLAGS_ACCESSIBILITY_MANAGER, "windowToken=" + windowToken);
}
synchronized (mLock) {
@@ -3196,8 +3210,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public long getRecommendedTimeoutMillis() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(
+ LOG_TAG + ".getRecommendedTimeoutMillis", FLAGS_ACCESSIBILITY_MANAGER);
}
synchronized(mLock) {
@@ -3214,8 +3229,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setWindowMagnificationConnection(
IWindowMagnificationConnection connection) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"connection=" + connection);
}
@@ -3249,9 +3266,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
- "host=" + host + ";embedded=" + embedded);
+ FLAGS_ACCESSIBILITY_MANAGER, "host=" + host + ";embedded=" + embedded);
}
synchronized (mLock) {
@@ -3261,8 +3278,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy",
+ FLAGS_ACCESSIBILITY_MANAGER, "token=" + token);
}
synchronized (mLock) {
@@ -3274,7 +3292,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Gets the stroke width of the focus rectangle.
* @return The stroke width.
*/
+ @Override
public int getFocusStrokeWidth() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -3286,7 +3308,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Gets the color of the focus rectangle.
* @return The color.
*/
+ @Override
public int getFocusColor() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -3314,6 +3340,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.println();
}
mA11yWindowManager.dump(fd, pw, args);
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.dump(fd, pw, args);
+ }
pw.println("Global client list info:{");
mGlobalClients.dump(pw, " Client list ");
pw.println(" Registered clients:{");
@@ -3350,9 +3379,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController");
- }
synchronized (mLock) {
return mMagnificationController.getFullScreenMagnificationController();
}
@@ -3360,11 +3386,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked",
- "serviceInfoChanged=" + serviceInfoChanged);
- }
-
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
@@ -3569,7 +3590,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
mDisplaysList.add(display);
if (mInputFilter != null) {
- mInputFilter.onDisplayChanged();
+ mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3591,7 +3612,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
if (mInputFilter != null) {
- mInputFilter.onDisplayChanged();
+ mInputFilter.onDisplayRemoved(displayId);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3891,11 +3912,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
- "displayId=" + displayId + ";region=" + region);
- }
-
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setGestureDetectionPassthroughRegionInternal,
@@ -3906,11 +3922,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
- "displayId=" + displayId + ";region=" + region);
- }
-
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setTouchExplorationPassthroughRegionInternal,
@@ -3939,7 +3950,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (userState.mUserId != mCurrentUserId) {
return;
}
-
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateFocusAppearanceDataLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
@@ -3949,7 +3963,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
- AccessibilityTraceManager getTraceManager() {
+ public AccessibilityTraceManager getTraceManager() {
return mTraceManager;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index dc2628f0bc0b..a7f0f1c825db 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -534,7 +534,8 @@ public class AccessibilitySecurityPolicy {
int servicePackageUid = serviceInfo.applicationInfo.uid;
if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
- servicePackageUid, serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
+ servicePackageUid, serviceInfo.packageName, null, null)
+ != AppOpsManager.MODE_ALLOWED) {
Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+ ": disallowed by AppOps");
@@ -559,17 +560,21 @@ public class AccessibilitySecurityPolicy {
return true;
}
- final int uid = resolveInfo.serviceInfo.applicationInfo.uid;
+ final int servicePackageUid = resolveInfo.serviceInfo.applicationInfo.uid;
+ final int callingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
+ final String attributionTag = service.getAttributionTag();
try {
// For the caller is system, just block the data to a11y services.
- if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+ if (OWN_PROCESS_ID == callingPid) {
return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- uid, packageName) == AppOpsManager.MODE_ALLOWED;
+ servicePackageUid, packageName, attributionTag, null)
+ == AppOpsManager.MODE_ALLOWED;
}
return mAppOpsManager.noteOp(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- uid, packageName) == AppOpsManager.MODE_ALLOWED;
+ servicePackageUid, packageName, attributionTag, null)
+ == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identityToken);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 7d75b738d818..467cab5fec04 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -53,10 +54,7 @@ import java.util.Set;
*/
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CONNECTION =
- LOG_TAG + ".IAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CLIENT =
- LOG_TAG + ".IAccessibilityServiceClient";
+
/*
Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
lists of bound and binding services. These are freed on user changes, but just in case it
@@ -137,8 +135,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void disableSelf() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".disableSelf");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("disableSelf", "");
}
synchronized (mLock) {
AccessibilityUserState userState = mUserStateWeakReference.get();
@@ -218,9 +216,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
return;
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", this + ", " + mId + ", "
- + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("init",
+ this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
} catch (RemoteException re) {
@@ -264,9 +262,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean setSoftKeyboardShowMode(int showMode) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardShowMode",
- "showMode=" + showMode);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setSoftKeyboardShowMode", "showMode=" + showMode);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -280,8 +277,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public int getSoftKeyboardShowMode() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSoftKeyboardShowMode");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getSoftKeyboardShowMode", "");
}
final AccessibilityUserState userState = mUserStateWeakReference.get();
return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
@@ -289,9 +286,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean switchToInputMethod(String imeId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".switchToInputMethod",
- "imeId=" + imeId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -311,8 +307,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean isAccessibilityButtonAvailable() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".isAccessibilityButtonAvailable");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("isAccessibilityButtonAvailable", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -373,9 +369,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
if (serviceInterface != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT
- + ".onFingerprintCapturingGesturesChanged", String.valueOf(active));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient(
+ "onFingerprintCapturingGesturesChanged", String.valueOf(active));
}
mServiceInterface.onFingerprintCapturingGesturesChanged(active);
} catch (RemoteException e) {
@@ -394,9 +390,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
if (serviceInterface != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onFingerprintGesture",
- String.valueOf(gesture));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture));
}
mServiceInterface.onFingerprintGesture(gesture);
} catch (RemoteException e) {
@@ -410,15 +405,17 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
+ if (wmTracingEnabled()) {
+ logTraceWM("isTouchOrFaketouchDevice", "");
+ }
if (motionEventInjector != null
&& mWindowManagerService.isTouchOrFaketouchDevice()) {
motionEventInjector.injectEvents(
gestureSteps.getList(), mServiceInterface, sequence, displayId);
} else {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onPerformGestureResult",
- sequence + ", false");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onPerformGestureResult", sequence + ", false");
}
mServiceInterface.onPerformGestureResult(sequence, false);
} catch (RemoteException re) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index 6396960281b7..8cf5547b05ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -60,7 +60,7 @@ final class AccessibilityShellCommand extends ShellCommand {
}
case "start-trace":
case "stop-trace":
- return mService.getTraceManager().onShellCommand(cmd);
+ return mService.getTraceManager().onShellCommand(cmd, this);
}
return -1;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
deleted file mode 100644
index 03914138cd42..000000000000
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
+++ /dev/null
@@ -1,66 +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.accessibility;
-
-/**
- * Interface to log accessibility trace.
- */
-public interface AccessibilityTrace {
- /**
- * Whether the trace is enabled.
- */
- boolean isA11yTracingEnabled();
-
- /**
- * Start tracing.
- */
- void startTrace();
-
- /**
- * Stop tracing.
- */
- void stopTrace();
-
- /**
- * Log one trace entry.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- */
- void logTrace(String where);
-
- /**
- * Log one trace entry.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- * @param callingParams The parameters for the method to be logged.
- */
- void logTrace(String where, String callingParams);
-
- /**
- * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
- * make screen content related requests use this API to log entry when receive callback.
- * @param timestamp The timestamp when a callback is received.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- * @param callingParams The parameters for the callback.
- * @param processId The process id of the calling component.
- * @param threadId The threadId of the calling component.
- * @param callingUid The calling uid of the callback.
- * @param callStack The call stack of the callback.
- */
- void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack);
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
index 6105e8a6724b..51e01ea58a35 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
@@ -15,72 +15,197 @@
*/
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_ALL;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_NONE;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+
+import android.accessibilityservice.AccessibilityTrace;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.os.Binder;
+import android.os.ShellCommand;
import com.android.server.wm.WindowManagerInternal;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* Manager of accessibility trace.
*/
-class AccessibilityTraceManager implements AccessibilityTrace {
+public class AccessibilityTraceManager implements AccessibilityTrace {
private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
private final AccessibilityManagerService mService;
+ private final Object mA11yMSLock;
+
+ private long mEnabledLoggingFlags;
+
+ private static AccessibilityTraceManager sInstance = null;
+
+ @MainThread
+ static AccessibilityTraceManager getInstance(
+ @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityTraceManager(a11yController, service, lock);
+ }
+ return sInstance;
+ }
- AccessibilityTraceManager(
+ private AccessibilityTraceManager(
@NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
- @NonNull AccessibilityManagerService service) {
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
mA11yController = a11yController;
mService = service;
+ mA11yMSLock = lock;
+ mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
}
@Override
public boolean isA11yTracingEnabled() {
- return mA11yController.isAccessibilityTracingEnabled();
+ synchronized (mA11yMSLock) {
+ return mEnabledLoggingFlags != FLAGS_LOGGING_NONE;
+ }
+ }
+
+ @Override
+ public boolean isA11yTracingEnabledForTypes(long typeIdFlags) {
+ synchronized (mA11yMSLock) {
+ return ((typeIdFlags & mEnabledLoggingFlags) != FLAGS_LOGGING_NONE);
+ }
+ }
+
+ @Override
+ public int getTraceStateForAccessibilityManagerClientState() {
+ int state = 0x0;
+ synchronized (mA11yMSLock) {
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CLIENT)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE)) {
+ state |= STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+ }
+ }
+ return state;
}
@Override
- public void startTrace() {
- if (!mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.startTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+ public void startTrace(long loggingTypes) {
+ if (loggingTypes == FLAGS_LOGGING_NONE) {
+ // Ignore start none request
+ return;
+ }
+
+ synchronized (mA11yMSLock) {
+ long oldEnabled = mEnabledLoggingFlags;
+ mEnabledLoggingFlags = loggingTypes;
+
+ if (needToNotifyClients(oldEnabled)) {
+ mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+ }
}
+
+ mA11yController.startTrace(loggingTypes);
}
@Override
public void stopTrace() {
- if (mA11yController.isAccessibilityTracingEnabled()) {
+ boolean stop = false;
+ synchronized (mA11yMSLock) {
+ stop = isA11yTracingEnabled();
+
+ long oldEnabled = mEnabledLoggingFlags;
+ mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
+
+ if (needToNotifyClients(oldEnabled)) {
+ mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+ }
+ }
+ if (stop) {
mA11yController.stopTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
}
}
@Override
- public void logTrace(String where) {
- logTrace(where, "");
+ public void logTrace(String where, long loggingTypes) {
+ logTrace(where, loggingTypes, "");
}
@Override
- public void logTrace(String where, String callingParams) {
- mA11yController.logTrace(where, callingParams, "".getBytes(),
- Binder.getCallingUid(), Thread.currentThread().getStackTrace());
+ public void logTrace(String where, long loggingTypes, String callingParams) {
+ if (isA11yTracingEnabledForTypes(loggingTypes)) {
+ mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(),
+ Binder.getCallingUid(), Thread.currentThread().getStackTrace(),
+ new HashSet<String>(Arrays.asList("logTrace")));
+ }
}
@Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack) {
- if (mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack,
- timestamp, processId, threadId);
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+ Set<String> ignoreElementList) {
+ if (isA11yTracingEnabledForTypes(loggingTypes)) {
+ mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(), callingUid,
+ callStack, timestamp, processId, threadId,
+ ((ignoreElementList == null) ? new HashSet<String>() : ignoreElementList));
}
}
- int onShellCommand(String cmd) {
+ private boolean needToNotifyClients(long otherTypesEnabled) {
+ return (mEnabledLoggingFlags & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES)
+ != (otherTypesEnabled & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES);
+ }
+
+ int onShellCommand(String cmd, ShellCommand shell) {
switch (cmd) {
case "start-trace": {
- startTrace();
+ String opt = shell.getNextOption();
+ if (opt == null) {
+ startTrace(FLAGS_LOGGING_ALL);
+ return 0;
+ }
+ List<String> types = new ArrayList<String>();
+ while (opt != null) {
+ switch (opt) {
+ case "-t": {
+ String type = shell.getNextArg();
+ while (type != null) {
+ types.add(type);
+ type = shell.getNextArg();
+ }
+ break;
+ }
+ default: {
+ shell.getErrPrintWriter().println(
+ "Error: option not recognized " + opt);
+ stopTrace();
+ return -1;
+ }
+ }
+ opt = shell.getNextOption();
+ }
+ long enabledTypes = AccessibilityTrace.getLoggingFlagsFromNames(types);
+ startTrace(enabledTypes);
return 0;
}
case "stop-trace": {
@@ -92,8 +217,29 @@ class AccessibilityTraceManager implements AccessibilityTrace {
}
void onHelp(PrintWriter pw) {
- pw.println(" start-trace");
- pw.println(" Start the debug tracing.");
+ pw.println(" start-trace [-t LOGGING_TYPE [LOGGING_TYPE...]]");
+ pw.println(" Start the debug tracing. If no option is present, full trace will be");
+ pw.println(" generated. Options are:");
+ pw.println(" -t: Only generate tracing for the logging type(s) specified here.");
+ pw.println(" LOGGING_TYPE can be any one of below:");
+ pw.println(" IAccessibilityServiceConnection");
+ pw.println(" IAccessibilityServiceClient");
+ pw.println(" IAccessibilityManager");
+ pw.println(" IAccessibilityManagerClient");
+ pw.println(" IAccessibilityInteractionConnection");
+ pw.println(" IAccessibilityInteractionConnectionCallback");
+ pw.println(" IRemoteMagnificationAnimationCallback");
+ pw.println(" IWindowMagnificationConnection");
+ pw.println(" IWindowMagnificationConnectionCallback");
+ pw.println(" WindowManagerInternal");
+ pw.println(" WindowsForAccessibilityCallback");
+ pw.println(" MagnificationCallbacks");
+ pw.println(" InputFilter");
+ pw.println(" Gesture");
+ pw.println(" AccessibilityService");
+ pw.println(" PMBroadcastReceiver");
+ pw.println(" UserBroadcastReceiver");
+ pw.println(" FingerprintGesture");
pw.println(" stop-trace");
pw.println(" Stop the debug tracing.");
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0fde0de59c07..c70bf73fc7f5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -392,7 +392,7 @@ class AccessibilityUserState {
return mBoundServices;
}
- int getClientStateLocked(boolean isUiAutomationRunning, boolean isTracingEnabled) {
+ int getClientStateLocked(boolean isUiAutomationRunning, int traceClientState) {
int clientState = 0;
final boolean a11yEnabled = isUiAutomationRunning
|| isHandlingAccessibilityEventsLocked();
@@ -408,9 +408,9 @@ class AccessibilityUserState {
if (mIsTextHighContrastEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
}
- if (isTracingEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED;
- }
+
+ clientState |= traceClientState;
+
return clientState;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index ff794691d2b4..b05dffef9c53 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
@@ -69,6 +71,7 @@ public class AccessibilityWindowManager {
private final AccessibilityEventSender mAccessibilityEventSender;
private final AccessibilitySecurityPolicy mSecurityPolicy;
private final AccessibilityUserManager mAccessibilityUserManager;
+ private final AccessibilityTraceManager mTraceManager;
// Connections and window tokens for cross-user windows
private final SparseArray<RemoteAccessibilityConnection>
@@ -151,6 +154,10 @@ public class AccessibilityWindowManager {
// In some cases, onWindowsForAccessibilityChanged will be called immediately in
// setWindowsForAccessibilityCallback. We'll lost windows if flag is false.
mTrackingWindows = true;
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=" + this);
+ }
result = mWindowManagerInternal.setWindowsForAccessibilityCallback(
mDisplayId, this);
if (!result) {
@@ -167,6 +174,10 @@ public class AccessibilityWindowManager {
*/
void stopTrackingWindowsLocked() {
if (mTrackingWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=null");
+ }
mWindowManagerInternal.setWindowsForAccessibilityCallback(
mDisplayId, null);
mTrackingWindows = false;
@@ -373,6 +384,20 @@ public class AccessibilityWindowManager {
}
}
+ /**
+ * Called when the display is reparented and becomes an embedded
+ * display.
+ *
+ * @param embeddedDisplayId The embedded display Id.
+ */
+ @Override
+ public void onDisplayReparented(int embeddedDisplayId) {
+ // Removes the un-used window observer for the embedded display.
+ synchronized (mLock) {
+ mDisplayWindowsObservers.remove(embeddedDisplayId);
+ }
+ }
+
private boolean shouldUpdateWindowsLocked(boolean forceSend,
@NonNull List<WindowInfo> windows) {
if (forceSend) {
@@ -844,19 +869,21 @@ public class AccessibilityWindowManager {
}
/**
- * Constructor for AccessibilityManagerService.
+ * Constructor for AccessibilityWindowManager.
*/
public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler,
@NonNull WindowManagerInternal windowManagerInternal,
@NonNull AccessibilityEventSender accessibilityEventSender,
@NonNull AccessibilitySecurityPolicy securityPolicy,
- @NonNull AccessibilityUserManager accessibilityUserManager) {
+ @NonNull AccessibilityUserManager accessibilityUserManager,
+ @NonNull AccessibilityTraceManager traceManager) {
mLock = lock;
mHandler = handler;
mWindowManagerInternal = windowManagerInternal;
mAccessibilityEventSender = accessibilityEventSender;
mSecurityPolicy = securityPolicy;
mAccessibilityUserManager = accessibilityUserManager;
+ mTraceManager = traceManager;
}
/**
@@ -957,6 +984,9 @@ public class AccessibilityWindowManager {
final int windowId;
boolean shouldComputeWindows = false;
final IBinder token = window.asBinder();
+ if (traceWMEnabled()) {
+ logTraceWM("getDisplayIdForWindow", "token=" + token);
+ }
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
@@ -1003,9 +1033,15 @@ public class AccessibilityWindowManager {
registerIdLocked(leashToken, windowId);
}
if (shouldComputeWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("computeWindowsForAccessibility", "displayId=" + displayId);
+ }
mWindowManagerInternal.computeWindowsForAccessibility(displayId);
}
-
+ if (traceWMEnabled()) {
+ logTraceWM("setAccessibilityIdToSurfaceMetadata",
+ "token=" + token + ";windowId=" + windowId);
+ }
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
return windowId;
}
@@ -1139,6 +1175,10 @@ public class AccessibilityWindowManager {
mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
if (binder != null) {
+ if (traceWMEnabled()) {
+ logTraceWM("setAccessibilityIdToSurfaceMetadata", "token=" + binder
+ + ";windowId=AccessibilityWindowInfo.UNDEFINED_WINDOW_ID");
+ }
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
}
@@ -1169,6 +1209,9 @@ public class AccessibilityWindowManager {
* @return The userId
*/
public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
+ if (traceWMEnabled()) {
+ logTraceWM("getWindowOwnerUserId", "token=" + windowToken);
+ }
return mWindowManagerInternal.getWindowOwnerUserId(windowToken);
}
@@ -1547,6 +1590,10 @@ public class AccessibilityWindowManager {
for (int i = 0; i < connectionList.size(); i++) {
final RemoteAccessibilityConnection connection = connectionList.get(i);
if (connection != null) {
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
+
try {
connection.getRemote().notifyOutsideTouch();
} catch (RemoteException re) {
@@ -1567,6 +1614,9 @@ public class AccessibilityWindowManager {
*/
public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+ if (traceWMEnabled()) {
+ logTraceWM("getDisplayIdForWindow", "token=" + windowToken);
+ }
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
return displayId;
}
@@ -1595,6 +1645,9 @@ public class AccessibilityWindowManager {
* @return The input focused windowId, or -1 if not found
*/
private int findFocusedWindowId(int userId) {
+ if (traceWMEnabled()) {
+ logTraceWM("getFocusedWindowToken", "");
+ }
final IBinder token = mWindowManagerInternal.getFocusedWindowToken();
synchronized (mLock) {
return findWindowIdLocked(userId, token);
@@ -1644,6 +1697,9 @@ public class AccessibilityWindowManager {
return;
}
}
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
try {
connection.getRemote().clearAccessibilityFocus();
} catch (RemoteException re) {
@@ -1666,6 +1722,25 @@ public class AccessibilityWindowManager {
return null;
}
+ private boolean traceWMEnabled() {
+ return mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ private void logTraceWM(String methodName, String params) {
+ mTraceManager.logTrace("WindowManagerInternal." + methodName,
+ FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
+
+ private boolean traceIntConnEnabled() {
+ return mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
+ private void logTraceIntConn(String methodName) {
+ mTraceManager.logTrace(
+ LOG_TAG + "." + methodName, FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
/**
* Associate the token of the embedded view hierarchy to the host view hierarchy.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index f5b0eb1eb5b6..95f3560dbbb8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
@@ -56,6 +57,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+ private final AccessibilityTraceManager mTrace;
private final Context mContext;
private final int mUserId;
@@ -63,13 +65,18 @@ public class AutoclickController extends BaseEventStreamTransformation {
private ClickScheduler mClickScheduler;
private ClickDelayObserver mClickDelayObserver;
- public AutoclickController(Context context, int userId) {
+ public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
+ mTrace = trace;
mContext = context;
mUserId = userId;
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mTrace.logTrace(LOG_TAG + ".onMotionEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (mClickScheduler == null) {
Handler handler = new Handler(mContext.getMainLooper());
@@ -89,6 +96,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mTrace.logTrace(LOG_TAG + ".onKeyEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";policyFlags=" + policyFlags);
+ }
if (mClickScheduler != null) {
if (KeyEvent.isModifierKey(event.getKeyCode())) {
mClickScheduler.updateMetaState(event.getMetaState());
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
index bc379c204d44..b8250c028f62 100644
--- a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -64,6 +66,10 @@ public class KeyboardInterceptor extends BaseEventStreamTransformation implement
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(FLAGS_INPUT_FILTER)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent",
+ FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags);
+ }
/*
* Certain keys have system-level behavior that affects accessibility services.
* Let that behavior settle before handling the keys
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 2673cd1ac3b8..5cbd1a208ce1 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.GestureStep;
import android.accessibilityservice.GestureDescription.TouchPoint;
@@ -68,6 +69,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
private final Handler mHandler;
private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
+ private final AccessibilityTraceManager mTrace;
private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
private IntArray mSequencesInProgress = new IntArray(5);
private boolean mIsDestroyed = false;
@@ -80,15 +82,17 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
/**
* @param looper A looper on the main thread to use for dispatching new events
*/
- public MotionEventInjector(Looper looper) {
+ public MotionEventInjector(Looper looper, AccessibilityTraceManager trace) {
mHandler = new Handler(looper, this);
+ mTrace = trace;
}
/**
* @param handler A handler to post messages. Exposes internal state for testing only.
*/
- public MotionEventInjector(Handler handler) {
+ public MotionEventInjector(Handler handler, AccessibilityTraceManager trace) {
mHandler = handler;
+ mTrace = trace;
}
/**
@@ -112,6 +116,12 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+ mTrace.logTrace(LOG_TAG + ".onMotionEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
// MotionEventInjector would cancel any injected gesture when any MotionEvent arrives.
// For user using an external device to control the pointer movement, it's almost
// impossible to perform the gestures. Any slightly unintended movement results in the
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 9547280018e4..6cd23fcfd0fb 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.Nullable;
import android.app.UiAutomation;
@@ -269,6 +270,14 @@ class UiAutomationManager {
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
+ AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
+ "serviceConnection=" + this + ";connectionId=" + mId
+ + "windowToken="
+ + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ }
serviceInterface.init(this, mId,
mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index d7bc04091181..74f0bcb4245c 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.gestures;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_GESTURE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_ENTER;
@@ -82,6 +84,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
implements GestureManifold.Listener {
static final boolean DEBUG = false;
+ private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER;
// Tag for logging received events.
private static final String LOG_TAG = "TouchExplorer";
@@ -254,6 +257,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onMotionEvent", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
super.onMotionEvent(event, rawEvent, policyFlags);
return;
@@ -308,6 +315,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onAccessibilityEvent",
+ LOGGING_FLAGS, "event=" + event);
+ }
final int eventType = event.getEventType();
if (eventType == TYPE_VIEW_HOVER_EXIT) {
@@ -346,6 +357,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTapAndHold", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
if (isSendMotionEventsEnabled()) {
@@ -362,6 +377,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTap", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
mAms.onTouchInteractionEnd();
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -394,6 +413,9 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureStarted() {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureStarted", LOGGING_FLAGS);
+ }
// We have to perform gesture detection, so
// clear the current state and try to detect.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -407,6 +429,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCompleted",
+ LOGGING_FLAGS, "event=" + gestureEvent);
+ }
endGestureDetection(true);
mSendTouchInteractionEndDelayed.cancel();
dispatchGesture(gestureEvent);
@@ -415,6 +441,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCancelled", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (mState.isGestureDetecting()) {
endGestureDetection(event.getActionMasked() == ACTION_UP);
return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1f66bfdb20c1..718da9e390ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -46,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.wm.WindowManagerInternal;
import java.util.Locale;
@@ -135,6 +138,10 @@ public class FullScreenMagnificationController {
*/
@GuardedBy("mLock")
boolean register() {
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=" + this);
+ }
mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, this);
if (!mRegistered) {
@@ -142,6 +149,10 @@ public class FullScreenMagnificationController {
return false;
}
mSpecAnimationBridge.setEnabled(true);
+ if (traceEnabled()) {
+ logTrace("getMagnificationRegion",
+ "displayID=" + mDisplayId + ";region=" + mMagnificationRegion);
+ }
// Obtain initial state.
mControllerCtx.getWindowManager().getMagnificationRegion(
mDisplayId, mMagnificationRegion);
@@ -162,6 +173,10 @@ public class FullScreenMagnificationController {
void unregister(boolean delete) {
if (mRegistered) {
mSpecAnimationBridge.setEnabled(false);
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=null");
+ }
mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, null);
mMagnificationRegion.setEmpty();
@@ -268,7 +283,7 @@ public class FullScreenMagnificationController {
}
@Override
- public void onRotationChanged(int rotation) {
+ public void onDisplaySizeChanged() {
// Treat as context change and reset
final Message m = PooledLambda.obtainMessage(
FullScreenMagnificationController::resetIfNeeded,
@@ -431,6 +446,10 @@ public class FullScreenMagnificationController {
void setForceShowMagnifiableBounds(boolean show) {
if (mRegistered) {
mForceShowMagnifiableBounds = show;
+ if (traceEnabled()) {
+ logTrace("setForceShowMagnifiableBounds",
+ "displayID=" + mDisplayId + ";show=" + show);
+ }
mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
mDisplayId, show);
}
@@ -1255,6 +1274,16 @@ public class FullScreenMagnificationController {
}
}
+ private boolean traceEnabled() {
+ return mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ private void logTrace(String methodName, String params) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -1320,6 +1349,13 @@ public class FullScreenMagnificationController {
mEnabled = enabled;
if (!mEnabled) {
mSentMagnificationSpec.clear();
+ if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal.setMagnificationSpec",
+ FLAGS_WINDOW_MANAGER_INTERNAL,
+ "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+ }
mControllerCtx.getWindowManager().setMagnificationSpec(
mDisplayId, mSentMagnificationSpec);
}
@@ -1367,6 +1403,13 @@ public class FullScreenMagnificationController {
}
mSentMagnificationSpec.setTo(spec);
+ if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal.setMagnificationSpec",
+ FLAGS_WINDOW_MANAGER_INTERNAL,
+ "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+ }
mControllerCtx.getWindowManager().setMagnificationSpec(
mDisplayId, mSentMagnificationSpec);
}
@@ -1455,6 +1498,7 @@ public class FullScreenMagnificationController {
public static class ControllerContext {
private final Context mContext;
private final AccessibilityManagerService mAms;
+ private final AccessibilityTraceManager mTrace;
private final WindowManagerInternal mWindowManager;
private final Handler mHandler;
private final Long mAnimationDuration;
@@ -1469,6 +1513,7 @@ public class FullScreenMagnificationController {
long animationDuration) {
mContext = context;
mAms = ams;
+ mTrace = ams.getTraceManager();
mWindowManager = windowManager;
mHandler = handler;
mAnimationDuration = animationDuration;
@@ -1491,6 +1536,14 @@ public class FullScreenMagnificationController {
}
/**
+ * @return AccessibilityTraceManager
+ */
+ @NonNull
+ public AccessibilityTraceManager getTraceManager() {
+ return mTrace;
+ }
+
+ /**
* @return WindowManagerInternal
*/
@NonNull
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f7d1b9a311ba..c3d8d4c2c96a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -61,6 +61,7 @@ import android.view.ViewConfiguration;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.gestures.GestureUtils;
/**
@@ -142,12 +143,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
+ AccessibilityTraceManager trace,
Callback callback,
boolean detectTripleTap,
boolean detectShortcutTrigger,
@NonNull WindowMagnificationPromptController promptController,
int displayId) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
Log.i(mLogTag,
"FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index f9aecd739d33..8aacafbd05c7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -411,8 +411,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
- mAms.getCurrentUserIdLocked(),
- this);
+ mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager());
}
return mWindowMagnificationMgr;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index bbe40b6faf74..19b339645557 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -20,11 +20,13 @@ import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_UP;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.BaseEventStreamTransformation;
import java.util.ArrayDeque;
@@ -99,14 +101,17 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
void onTripleTapped(int displayId, int mode);
}
+ private final AccessibilityTraceManager mTrace;
protected final Callback mCallback;
protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
boolean detectShortcutTrigger,
+ AccessibilityTraceManager trace,
@NonNull Callback callback) {
mDisplayId = displayId;
mDetectTripleTap = detectTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
+ mTrace = trace;
mCallback = callback;
mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
@@ -118,6 +123,12 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
if (DEBUG_ALL) {
Slog.i(mLogTag, "onMotionEvent(" + event + ")");
}
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+ mTrace.logTrace("MagnificationGestureHandler.onMotionEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (DEBUG_EVENT_STREAM) {
storeEventInto(mDebugInputEventHistory, event);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 993027d1ca3c..527742536480 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.os.IBinder.DeathRecipient;
import android.annotation.NonNull;
@@ -27,6 +30,8 @@ import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
/**
* A wrapper of {@link IWindowMagnificationConnection}.
*/
@@ -36,9 +41,12 @@ class WindowMagnificationConnectionWrapper {
private static final String TAG = "WindowMagnificationConnectionWrapper";
private final @NonNull IWindowMagnificationConnection mConnection;
+ private final @NonNull AccessibilityTraceManager mTrace;
- WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+ WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
+ @NonNull AccessibilityTraceManager trace) {
mConnection = connection;
+ mTrace = trace;
}
//Should not use this instance anymore after calling it.
@@ -52,9 +60,15 @@ class WindowMagnificationConnectionWrapper {
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".enableWindowMagnification",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+ + ";centerY=" + centerY + ";callback=" + callback);
+ }
try {
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
- transformToRemoteCallback(callback));
+ transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -65,6 +79,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean setScale(int displayId, float scale) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";scale=" + scale);
+ }
try {
mConnection.setScale(displayId, scale);
} catch (RemoteException e) {
@@ -78,8 +96,14 @@ class WindowMagnificationConnectionWrapper {
boolean disableWindowMagnification(int displayId,
@Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".disableWindowMagnification",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";callback=" + callback);
+ }
try {
- mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback));
+ mConnection.disableWindowMagnification(displayId,
+ transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling disableWindowMagnification()", e);
@@ -90,6 +114,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";offsetX=" + offsetX + ";offsetY=" + offsetY);
+ }
try {
mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
} catch (RemoteException e) {
@@ -102,6 +130,11 @@ class WindowMagnificationConnectionWrapper {
}
boolean showMagnificationButton(int displayId, int magnificationMode) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".showMagnificationButton",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";mode=" + magnificationMode);
+ }
try {
mConnection.showMagnificationButton(displayId, magnificationMode);
} catch (RemoteException e) {
@@ -114,6 +147,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean removeMagnificationButton(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".removeMagnificationButton",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+ }
try {
mConnection.removeMagnificationButton(displayId);
} catch (RemoteException e) {
@@ -126,6 +163,14 @@ class WindowMagnificationConnectionWrapper {
}
boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + ".setConnectionCallback",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "callback=" + connectionCallback);
+ }
try {
mConnection.setConnectionCallback(connectionCallback);
} catch (RemoteException e) {
@@ -139,25 +184,38 @@ class WindowMagnificationConnectionWrapper {
private static @Nullable
IRemoteMagnificationAnimationCallback transformToRemoteCallback(
- MagnificationAnimationCallback callback) {
+ MagnificationAnimationCallback callback, AccessibilityTraceManager trace) {
if (callback == null) {
return null;
}
- return new RemoteAnimationCallback(callback);
+ return new RemoteAnimationCallback(callback, trace);
}
private static class RemoteAnimationCallback extends
IRemoteMagnificationAnimationCallback.Stub {
-
private final MagnificationAnimationCallback mCallback;
+ private final AccessibilityTraceManager mTrace;
- RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) {
+ RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback,
+ @NonNull AccessibilityTraceManager trace) {
mCallback = callback;
+ mTrace = trace;
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+ mTrace.logTrace("RemoteAnimationCallback.constructor",
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "callback=" + callback);
+ }
}
@Override
public void onResult(boolean success) throws RemoteException {
mCallback.onResult(success);
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+ mTrace.logTrace("RemoteAnimationCallback.onResult",
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "success=" + success);
+ }
+
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 4fb9a03b8ac1..b26d36493a3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -34,6 +34,7 @@ import android.view.Display;
import android.view.MotionEvent;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.gestures.MultiTap;
import com.android.server.accessibility.gestures.MultiTapAndHold;
@@ -89,9 +90,10 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
public WindowMagnificationGestureHandler(@UiContext Context context,
WindowMagnificationManager windowMagnificationMgr,
+ AccessibilityTraceManager trace,
Callback callback,
boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
Slog.i(mLogTag,
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 938cb73dac79..7a111d80b42e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -39,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -111,11 +115,14 @@ public class WindowMagnificationManager implements
}
private final Callback mCallback;
+ private final AccessibilityTraceManager mTrace;
- public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback) {
+ public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
+ AccessibilityTraceManager trace) {
mContext = context;
mUserId = userId;
mCallback = callback;
+ mTrace = trace;
}
/**
@@ -135,7 +142,7 @@ public class WindowMagnificationManager implements
mConnectionWrapper = null;
}
if (connection != null) {
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
}
if (mConnectionWrapper != null) {
@@ -197,7 +204,10 @@ public class WindowMagnificationManager implements
}
}
}
-
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+ }
final long identity = Binder.clearCallingIdentity();
try {
final StatusBarManagerInternal service = LocalServices.getService(
@@ -511,6 +521,12 @@ public class WindowMagnificationManager implements
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";bounds=" + bounds);
+ }
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -527,11 +543,23 @@ public class WindowMagnificationManager implements
@Override
public void onChangeMagnificationMode(int displayId, int magnificationMode)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";mode=" + magnificationMode);
+ }
//TODO: Uses this method to change the magnification mode on non-default display.
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";source=" + sourceBounds);
+ }
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -543,11 +571,23 @@ public class WindowMagnificationManager implements
@Override
public void onPerformScaleAction(int displayId, float scale) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";scale=" + scale);
+ }
mCallback.onPerformScaleAction(displayId, scale);
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId);
+ }
mCallback.onAccessibilityActionPerformed(displayId);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index de5f47dd5dbb..012c5b17c066 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -618,7 +618,7 @@ public final class AutofillManagerService
+ " for " + durationMs + "ms");
enforceCallingPermissionForManagement();
- Preconditions.checkNotNull(serviceName);
+ Objects.requireNonNull(serviceName);
if (durationMs > MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS) {
throw new IllegalArgumentException("Max duration is "
+ MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS + " (called with " + durationMs + ")");
@@ -929,7 +929,7 @@ public final class AutofillManagerService
void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
long expiration) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
synchronized (mLock) {
AutofillDisabledInfo info =
getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
@@ -939,7 +939,7 @@ public final class AutofillManagerService
void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
long expiration) {
- Preconditions.checkNotNull(componentName);
+ Objects.requireNonNull(componentName);
synchronized (mLock) {
AutofillDisabledInfo info =
getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
@@ -949,7 +949,7 @@ public final class AutofillManagerService
boolean isAutofillDisabledLocked(@UserIdInt int userId,
@NonNull ComponentName componentName) {
- Preconditions.checkNotNull(componentName);
+ Objects.requireNonNull(componentName);
final boolean disabled;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
@@ -959,7 +959,7 @@ public final class AutofillManagerService
}
long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
final Long expiration;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
@@ -971,7 +971,7 @@ public final class AutofillManagerService
@Nullable
ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
@NonNull String packageName) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
final ArrayMap<String, Long> disabledList;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
@@ -1593,8 +1593,8 @@ public final class AutofillManagerService
@NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
- activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
- appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
+ activityToken = Objects.requireNonNull(activityToken, "activityToken");
+ appCallback = Objects.requireNonNull(appCallback, "appCallback");
boolean restored = false;
synchronized (mLock) {
@@ -1693,7 +1693,7 @@ public final class AutofillManagerService
@Override
public void onPendingSaveUi(int operation, IBinder token) {
- Preconditions.checkNotNull(token, "token");
+ Objects.requireNonNull(token, "token");
Preconditions.checkArgument(operation == AutofillManager.PENDING_UI_OPERATION_CANCEL
|| operation == AutofillManager.PENDING_UI_OPERATION_RESTORE,
"invalid operation: %d", operation);
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
new file mode 100644
index 000000000000..715697d82cad
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
@@ -0,0 +1,293 @@
+/*
+ * 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.autofill;
+
+import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.SaveInfo;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Maintains a client suggestions session with the
+ * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
+ *
+ */
+final class ClientSuggestionsSession {
+
+ private static final String TAG = "ClientSuggestionsSession";
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
+
+ private final int mSessionId;
+ private final IAutoFillManagerClient mClient;
+ private final Handler mHandler;
+ private final ComponentName mComponentName;
+
+ private final RemoteFillService.FillServiceCallbacks mCallbacks;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private AndroidFuture<FillResponse> mPendingFillRequest;
+ @GuardedBy("mLock")
+ private int mPendingFillRequestId = INVALID_REQUEST_ID;
+
+ ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
+ ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
+ mSessionId = sessionId;
+ mClient = client;
+ mHandler = handler;
+ mComponentName = componentName;
+ mCallbacks = callbacks;
+ }
+
+ void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
+ final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+ final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
+ final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
+
+ mHandler.post(() -> {
+ if (sVerbose) {
+ Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
+ }
+
+ try {
+ mClient.requestFillFromClient(requestId, inlineRequest,
+ new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
+ } catch (RemoteException e) {
+ fillRequest.completeExceptionally(e);
+ }
+ });
+
+ fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+ futureRef.set(fillRequest);
+
+ synchronized (mLock) {
+ mPendingFillRequest = fillRequest;
+ mPendingFillRequestId = requestId;
+ }
+
+ fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
+ synchronized (mLock) {
+ mPendingFillRequest = null;
+ mPendingFillRequestId = INVALID_REQUEST_ID;
+ }
+ if (err == null) {
+ processAutofillId(res);
+ mCallbacks.onFillRequestSuccess(requestId, res,
+ mComponentName.getPackageName(), flags);
+ } else {
+ Slog.e(TAG, "Error calling on client fill request", err);
+ if (err instanceof TimeoutException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ mCallbacks.onFillRequestTimeout(requestId);
+ } else if (err instanceof CancellationException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ } else {
+ mCallbacks.onFillRequestFailure(requestId, err.getMessage());
+ }
+ }
+ }));
+ }
+
+ /**
+ * Gets the application info for the component.
+ */
+ @Nullable
+ static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
+ try {
+ ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
+ comp.getPackageName(),
+ PackageManager.GET_META_DATA,
+ userId);
+ if (si != null) {
+ return si;
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Gets the user-visible name of the application.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
+ return appInfo == null ? null : appInfo.loadSafeLabel(
+ context.getPackageManager(), 0 /* do not ellipsize */,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
+ }
+
+ /**
+ * Gets the user-visible icon of the application.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
+ return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
+ }
+
+ int cancelCurrentRequest() {
+ synchronized (mLock) {
+ return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+ ? mPendingFillRequestId
+ : INVALID_REQUEST_ID;
+ }
+ }
+
+ /**
+ * The {@link AutofillId} which the client gets from its view is not contain the session id,
+ * but Autofill framework is using the {@link AutofillId} with a session id. So before using
+ * those ids in the Autofill framework, applies the current session id.
+ *
+ * @param res which response need to apply for a session id
+ */
+ private void processAutofillId(FillResponse res) {
+ if (res == null) {
+ return;
+ }
+
+ final List<Dataset> datasets = res.getDatasets();
+ if (datasets != null && !datasets.isEmpty()) {
+ for (int i = 0; i < datasets.size(); i++) {
+ final Dataset dataset = datasets.get(i);
+ if (dataset != null) {
+ applySessionId(dataset.getFieldIds());
+ }
+ }
+ }
+
+ final SaveInfo saveInfo = res.getSaveInfo();
+ if (saveInfo != null) {
+ applySessionId(saveInfo.getOptionalIds());
+ applySessionId(saveInfo.getRequiredIds());
+ applySessionId(saveInfo.getSanitizerValues());
+ applySessionId(saveInfo.getTriggerId());
+ }
+ }
+
+ private void applySessionId(List<AutofillId> ids) {
+ if (ids == null || ids.isEmpty()) {
+ return;
+ }
+
+ for (int i = 0; i < ids.size(); i++) {
+ applySessionId(ids.get(i));
+ }
+ }
+
+ private void applySessionId(AutofillId[][] ids) {
+ if (ids == null) {
+ return;
+ }
+ for (int i = 0; i < ids.length; i++) {
+ applySessionId(ids[i]);
+ }
+ }
+
+ private void applySessionId(AutofillId[] ids) {
+ if (ids == null) {
+ return;
+ }
+ for (int i = 0; i < ids.length; i++) {
+ applySessionId(ids[i]);
+ }
+ }
+
+ private void applySessionId(AutofillId id) {
+ if (id == null) {
+ return;
+ }
+ id.setSessionId(mSessionId);
+ }
+
+ private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
+ if (signal == null) {
+ return;
+ }
+ try {
+ signal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error requesting a cancellation", e);
+ }
+ }
+
+ private class FillCallbackImpl extends IFillCallback.Stub {
+ final AndroidFuture<FillResponse> mFillRequest;
+ final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
+ final AtomicReference<ICancellationSignal> mCancellationSink;
+
+ FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
+ AtomicReference<AndroidFuture<FillResponse>> futureRef,
+ AtomicReference<ICancellationSignal> cancellationSink) {
+ mFillRequest = fillRequest;
+ mFutureRef = futureRef;
+ mCancellationSink = cancellationSink;
+ }
+
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ AndroidFuture<FillResponse> future = mFutureRef.get();
+ if (future != null && future.isCancelled()) {
+ dispatchCancellationSignal(cancellation);
+ } else {
+ mCancellationSink.set(cancellation);
+ }
+ }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ mFillRequest.complete(response);
+ }
+
+ @Override
+ public void onFailure(int requestId, CharSequence message) {
+ String errorMessage = message == null ? "" : String.valueOf(message);
+ mFillRequest.completeExceptionally(
+ new RuntimeException(errorMessage));
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 078d908684bc..11df87135ee3 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -53,6 +54,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -346,6 +348,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
+ @Nullable
+ private ClientSuggestionsSession mClientSuggestionsSession;
+
void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input
@@ -416,6 +421,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/** Whether the current {@link FillResponse} is expired. */
@GuardedBy("mLock")
private boolean mExpiredResponse;
+
+ /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
+ @GuardedBy("mLock")
+ private boolean mClientSuggestionsEnabled;
}
/**
@@ -441,13 +450,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
} : null;
}
- void maybeRequestFillLocked() {
+ void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
+ mPendingFillRequest = null;
+ mWaitForInlineRequest = inlineRequest != null;
+ mPendingInlineSuggestionsRequest = inlineRequest;
+ }
+
+ void maybeRequestFillFromServiceLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -457,9 +472,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
- mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
+ mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(),
+ mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ }
}
mRemoteFillService.onFillRequest(mPendingFillRequest);
@@ -566,7 +584,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/*inlineSuggestionsRequest=*/null);
mPendingFillRequest = request;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
}
if (mActivityToken != null) {
@@ -728,30 +746,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Cancels the last request sent to the {@link #mRemoteFillService}.
+ * Cancels the last request sent to the {@link #mRemoteFillService} or the
+ * {@link #mClientSuggestionsSession}.
*/
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
- if (mRemoteFillService == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+ if (mRemoteFillService == null && mClientSuggestionsSession == null) {
+ wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
+ + "client suggestions session. mForAugmentedAutofillOnly: %s",
+ mSessionFlags.mAugmentedAutofillOnly);
return;
}
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
+ if (mRemoteFillService != null) {
+ final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
+
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
+ }
}
}
}
+
+ if (mClientSuggestionsSession != null) {
+ mClientSuggestionsSession.cancelCurrentRequest();
+ }
}
private boolean isViewFocusedLocked(int flags) {
@@ -816,17 +843,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// structure is taken. This causes only one fill request per burst of focus changes.
cancelCurrentRequestLocked();
- // Only ask IME to create inline suggestions request if Autofill provider supports it and
- // the render service is available except the autofill is triggered manually and the view
- // is also not focused.
+ // Only ask IME to create inline suggestions request when
+ // 1. Autofill provider supports it or client enabled client suggestions.
+ // 2. The render service is available.
+ // 3. The view is focused. (The view may not be focused if the autofill is triggered
+ // manually.)
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if (mSessionFlags.mInlineSupportedByService
+ if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
&& remoteRenderService != null
&& isViewFocusedLocked(flags)) {
- Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
- mAssistReceiver.newAutofillRequestLocked(viewState,
- /* isInlineRequest= */ true);
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ final int finalRequestId = requestId;
+ inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
+ // Using client suggestions
+ synchronized (mLock) {
+ onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
+ }
+ viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+ };
+ } else {
+ inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
+ viewState, /* isInlineRequest= */ true);
+ }
if (inlineSuggestionsRequestConsumer != null) {
final AutofillId focusedId = mCurrentViewId;
final int requestIdCopy = requestId;
@@ -842,11 +882,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
+ } else if (mSessionFlags.mClientSuggestionsEnabled) {
+ // Request client suggestions for the dropdown mode
+ onClientFillRequestLocked(requestId, null);
} else {
mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
}
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ // Using client suggestions, unnecessary request AssistStructure
+ return;
+ }
+
// Now request the assist structure data.
+ requestAssistStructureLocked(requestId, flags);
+ }
+
+ @GuardedBy("mLock")
+ private void requestAssistStructureLocked(int requestId, int flags) {
try {
final Bundle receiverExtras = new Bundle();
receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
@@ -895,10 +948,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
+
synchronized (mLock) {
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
+ mSessionFlags.mClientSuggestionsEnabled =
+ (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
setClientLocked(client);
}
@@ -1010,12 +1066,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
- processNullResponseLocked(requestId, requestFlags);
+ processNullResponseOrFallbackLocked(requestId, requestFlags);
return;
}
fieldClassificationIds = response.getFieldClassificationIds();
- if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
+ if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
+ && !mService.isFieldClassificationEnabledLocked()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestId, requestFlags);
return;
@@ -1094,6 +1151,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ @GuardedBy("mLock")
+ private void processNullResponseOrFallbackLocked(int requestId, int flags) {
+ if (!mSessionFlags.mClientSuggestionsEnabled) {
+ processNullResponseLocked(requestId, flags);
+ return;
+ }
+
+ // fallback to the default platform password manager
+ mSessionFlags.mClientSuggestionsEnabled = false;
+
+ final InlineSuggestionsRequest inlineRequest =
+ (mLastInlineSuggestionsRequest != null
+ && mLastInlineSuggestionsRequest.first == requestId)
+ ? mLastInlineSuggestionsRequest.second : null;
+ mAssistReceiver.newAutofillRequestLocked(inlineRequest);
+ requestAssistStructureLocked(requestId,
+ flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
+ return;
+ }
+
// FillServiceCallbacks
@Override
public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
@@ -3042,13 +3119,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
filterText = value.getTextValue().toString();
}
- final CharSequence serviceLabel;
- final Drawable serviceIcon;
+ final CharSequence targetLabel;
+ final Drawable targetIcon;
synchronized (mLock) {
- serviceLabel = mService.getServiceLabelLocked();
- serviceIcon = mService.getServiceIconLocked();
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
+ mService.getUserId());
+ targetLabel = ClientSuggestionsSession.getAppLabelLocked(
+ mService.getMaster().getContext(), appInfo);
+ targetIcon = ClientSuggestionsSession.getAppIconLocked(
+ mService.getMaster().getContext(), appInfo);
+ } else {
+ targetLabel = mService.getServiceLabelLocked();
+ targetIcon = mService.getServiceIconLocked();
+ }
}
- if (serviceLabel == null || serviceIcon == null) {
+ if (targetLabel == null || targetIcon == null) {
wtf(null, "onFillReady(): no service label or icon");
return;
}
@@ -3068,7 +3154,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- serviceLabel, serviceIcon, this, id, mCompatMode);
+ targetLabel, targetIcon, this, id, mCompatMode);
mService.logDatasetShown(id, mClientState);
@@ -3115,6 +3201,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
+ final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
+ if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
+ || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
+ if (sDebug) {
+ Slog.d(TAG, "Inline suggestions not supported for "
+ + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
+ + ". Falling back to dropdown.");
+ }
+ return false;
+ }
+
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService == null) {
@@ -3123,7 +3220,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+ new InlineFillUi.InlineFillUiInfo(request, focusedId,
filterText, remoteRenderService, userId, id);
InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
new InlineFillUi.InlineSuggestionUiCallback() {
@@ -3725,6 +3822,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ @GuardedBy("mLock")
+ private void onClientFillRequestLocked(int requestId,
+ InlineSuggestionsRequest inlineSuggestionsRequest) {
+ if (mClientSuggestionsSession == null) {
+ mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
+ mComponentName, this);
+ }
+
+ if (mContexts == null) {
+ mContexts = new ArrayList<>(1);
+ }
+
+ if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
+ inlineSuggestionsRequest = null;
+ }
+
+ mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
+ }
+
/**
* The result of checking whether to show the save dialog, when session can be saved.
*
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 9ee0159e903a..1a5d91c8cca5 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4369,7 +4369,7 @@ public class UserBackupManagerService {
return OperationType.BACKUP;
}
- long oldCallingId = Binder.clearCallingIdentity();
+ final long oldCallingId = Binder.clearCallingIdentity();
try {
IBackupTransport transport = transportClient.connectOrThrow(
/* caller */ "BMS.getOperationTypeFromTransport");
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b7feb6306c99..4127556bb550 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -137,6 +137,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -145,6 +147,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@@ -182,6 +185,11 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
+ private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ static {
+ sDateFormat.setTimeZone(TimeZone.getDefault());
+ }
+
private final CompanionDeviceManagerImpl mImpl;
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
private PowerWhitelistManager mPowerWhitelistManager;
@@ -641,7 +649,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
association.getDeviceMacAddress(),
association.getPackageName(),
association.getDeviceProfile(),
- active, /* notifyOnDeviceNearby */
+ active /* notifyOnDeviceNearby */,
association.getTimeApprovedMs());
} else {
return association;
@@ -721,12 +729,40 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
synchronized (mLock) {
for (UserInfo user : getAllUsers()) {
forEach(mCachedAssociations.get(user.id), a -> {
- fout.append(" ")
- .append("u").append("" + a.getUserId()).append(": ")
- .append(a.getPackageName()).append(" - ")
- .append(a.getDeviceMacAddress()).append('\n');
+ fout.append(" ").append(a.toString()).append('\n');
});
}
+
+ }
+ fout.append("Currently Connected Devices:").append('\n');
+ for (int i = 0, size = mCurrentlyConnectedDevices.size(); i < size; i++) {
+ fout.append(" ").append(mCurrentlyConnectedDevices.get(i)).append('\n');
+ }
+
+ fout.append("Devices Last Nearby:").append('\n');
+ for (int i = 0, size = mDevicesLastNearby.size(); i < size; i++) {
+ String device = mDevicesLastNearby.keyAt(i);
+ Date time = mDevicesLastNearby.valueAt(i);
+ fout.append(" ").append(device).append(" -> ")
+ .append(sDateFormat.format(time)).append('\n');
+ }
+
+ fout.append("Discovery Service State:").append('\n');
+ for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
+ int userId = mServiceConnectors.keyAt(i);
+ fout.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mServiceConnectors.valueAt(i)))
+ .append('\n');
+ }
+
+ fout.append("Device Listener Services State:").append('\n');
+ for (int i = 0, size = mDeviceListenerServiceConnectors.size(); i < size; i++) {
+ int userId = mDeviceListenerServiceConnectors.keyAt(i);
+ fout.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mDeviceListenerServiceConnectors.valueAt(i)))
+ .append('\n');
}
}
}
@@ -777,7 +813,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
+ " for " + association
+ " - profile still present in " + otherAssociationWithDeviceProfile);
} else {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mRoleManager.removeRoleHolderAsUser(
association.getDeviceProfile(),
@@ -905,7 +941,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
.getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
Signature[] signatures = mPackageManagerInternal
- .getPackage(packageName).getSigningDetails().signatures;
+ .getPackage(packageName).getSigningDetails().getSignatures();
String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
Set<String> sameOemPackageCerts =
@@ -1043,7 +1079,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private List<UserInfo> getAllUsers() {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getUsers();
} finally {
@@ -1059,7 +1095,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private Set<Association> getAllAssociations() {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
ArraySet<Association> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
@@ -1071,6 +1107,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
+
private Set<Association> getAllAssociations(
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return CollectionUtils.filter(
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 55b982b40611..282b868a4ecb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -107,6 +107,7 @@ java_library_static {
":display-device-config",
":display-layout-config",
":device-state-config",
+ ":guiconstants_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -152,7 +153,7 @@ java_library_static {
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-V1-java",
- "android.hardware.soundtrigger-V2.3-java",
+ "android.hardware.soundtrigger-V2.4-java",
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 95186ee444e4..d7491ac82ebc 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,6 +30,7 @@ import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
@@ -69,7 +70,6 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
PACKAGE_BROWSER,
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
PACKAGE_PERMISSION_CONTROLLER,
- PACKAGE_DOCUMENTER,
PACKAGE_CONFIGURATOR,
PACKAGE_INCIDENT_REPORT_APPROVER,
PACKAGE_APP_PREDICTOR,
@@ -736,7 +736,7 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
* signing history for {@code serverUid} and with the {@code capability} specified.
*/
public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
- @PackageParser.SigningDetails.CertCapabilities int capability);
+ @CertCapabilities int capability);
/**
* Get appIds of all available apps which specified android:sharedUserId in the manifest.
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 85ff2be43be4..b4912bbc898d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -614,8 +614,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
// waive WRITE_SECURE_SETTINGS permission check
final long callingIdentity = Binder.clearCallingIdentity();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
- Binder.restoreCallingIdentity(callingIdentity);
+ try {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.BLUETOOTH_ON, value);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
/**
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index ac43fbd4f976..e4a30ad5b36c 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -402,7 +402,7 @@ public final class SensorPrivacyService extends SystemService {
return;
}
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
onSensorUseStarted(uid, packageName, sensor);
} finally {
@@ -750,7 +750,7 @@ public final class SensorPrivacyService extends SystemService {
}
if (!enable) {
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
// Remove any notifications prompting the user to disable sensory privacy
NotificationManager notificationManager =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 69c29269b7a9..ab6b8a21ee88 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -455,12 +455,6 @@ class StorageManagerService extends IStorageManager.Stub
"(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
- /** Automotive device unlockes users before system boot complete and this requires special
- * handling as vold reset can lead into race conditions. When this is set, all users unlocked
- * in {@code UserManager} level are unlocked after vold reset.
- */
- private final boolean mIsAutomotive;
-
private VolumeInfo findVolumeByIdOrThrow(String id) {
synchronized (mLock) {
final VolumeInfo vol = mVolumes.get(id);
@@ -1133,9 +1127,7 @@ class StorageManagerService extends IStorageManager.Stub
mVold.onUserStarted(userId);
mStoraged.onUserStarted(userId);
}
- if (mIsAutomotive) {
- restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
- }
+ restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
mStorageManagerInternal.onReset(mVold);
} catch (Exception e) {
@@ -1219,13 +1211,11 @@ class StorageManagerService extends IStorageManager.Stub
// Record user as started so newly mounted volumes kick off events
// correctly, then synthesize events for any already-mounted volumes.
synchronized (mLock) {
- if (mIsAutomotive) {
- for (int unlockedUser : mSystemUnlockedUsers) {
- if (unlockedUser == userId) {
- // This can happen as restoreAllUnlockedUsers can double post the message.
- Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
- return;
- }
+ for (int unlockedUser : mSystemUnlockedUsers) {
+ if (unlockedUser == userId) {
+ // This can happen as restoreAllUnlockedUsers can double post the message.
+ Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
+ return;
}
}
for (int i = 0; i < mVolumes.size(); i++) {
@@ -1551,17 +1541,19 @@ class StorageManagerService extends IStorageManager.Stub
}
if (vol.type == VolumeInfo.TYPE_EMULATED) {
+ if (!mStorageSessionController.supportsExternalStorage(vol.mountUserId)) {
+ Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+ + Integer.toString(vol.mountUserId)
+ + " does not support external storage.");
+ return;
+ }
+
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
- if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
- && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
- Slog.v(TAG, "Found primary storage at " + vol);
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
- mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
-
- } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
+ if ((Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
+ && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id))
+ || Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
@@ -1922,9 +1914,6 @@ class StorageManagerService extends IStorageManager.Stub
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
-
- mIsAutomotive = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
}
private void start() {
@@ -3461,48 +3450,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- @Override
- public void notifyAppIoBlocked(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- enforceExternalStorageService();
-
- mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
- }
-
- @Override
- public void notifyAppIoResumed(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- enforceExternalStorageService();
-
- mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
- }
-
- @Override
- public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- return isAppIoBlocked(uid);
- }
-
-
- private boolean isAppIoBlocked(int uid) {
- return mStorageSessionController.isAppIoBlocked(uid);
- }
-
- /**
- * Enforces that the caller is the {@link ExternalStorageService}
- *
- * @throws SecurityException if the caller doesn't have the
- * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
- * {@link ExternalStorageService}
- */
- private void enforceExternalStorageService() {
- enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
- int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
- if (callingAppId != mMediaStoreAuthorityAppId) {
- throw new SecurityException("Only the ExternalStorageService is permitted");
- }
- }
-
/**
* Returns PendingIntent which can be used by Apps with MANAGE_EXTERNAL_STORAGE permission
* to launch the manageSpaceActivity of the App specified by packageName.
@@ -3517,7 +3464,7 @@ class StorageManagerService extends IStorageManager.Stub
// We want to call the manageSpaceActivity as a SystemService and clear identity
// of the calling App
int originalUid = Binder.getCallingUidOrThrow();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
@@ -3550,6 +3497,46 @@ class StorageManagerService extends IStorageManager.Stub
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) {
+ enforceExternalStorageService();
+
+ mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
+ }
+
+ @Override
+ public void notifyAppIoResumed(String volumeUuid, int uid, int tid, int reason) {
+ enforceExternalStorageService();
+
+ mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
+ }
+
+ @Override
+ public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
+ @StorageManager.AppIoBlockedReason int reason) {
+ return isAppIoBlocked(uid);
+ }
+
+
+ private boolean isAppIoBlocked(int uid) {
+ return mStorageSessionController.isAppIoBlocked(uid);
+ }
+
+ /**
+ * Enforces that the caller is the {@link ExternalStorageService}
+ *
+ * @throws SecurityException if the caller doesn't have the
+ * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
+ * {@link ExternalStorageService}
+ */
+ private void enforceExternalStorageService() {
+ enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
+ int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+ if (callingAppId != mMediaStoreAuthorityAppId) {
+ throw new SecurityException("Only the ExternalStorageService is permitted");
+ }
+ }
/** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
@@ -4599,7 +4586,6 @@ class StorageManagerService extends IStorageManager.Stub
pw.println();
pw.println("Local unlocked users: " + mLocalUnlockedUsers);
pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
- pw.println("isAutomotive:" + mIsAutomotive);
}
synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a6a8cf018eef..76b55812db0e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -60,13 +60,14 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.UserInfo;
import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
import android.os.Bundle;
@@ -1833,6 +1834,11 @@ public class AccountManagerService
+ ", skipping since the account already exists");
return false;
}
+ if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ + ", skipping since more than 50 accounts on device exist");
+ return false;
+ }
long accountId = accounts.accountsDb.insertCeAccount(account, password);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
@@ -4871,9 +4877,7 @@ public class AccountManagerService
int targetUid = targetActivityInfo.applicationInfo.uid;
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (!isExportedSystemActivity(targetActivityInfo)
- && !pmi.hasSignatureCapability(
- targetUid, authUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ && !pmi.hasSignatureCapability(targetUid, authUid, CertCapabilities.AUTH)) {
String pkgName = targetActivityInfo.packageName;
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
@@ -5245,7 +5249,7 @@ public class AccountManagerService
logStatement.bindLong(6, userDebugDbInsertionPoint);
try {
logStatement.execute();
- } catch (IllegalStateException e) {
+ } catch (IllegalStateException | SQLiteFullException e) {
// Guard against crash, DB can already be closed
// since this statement is executed on a handler thread
Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
@@ -5626,8 +5630,7 @@ public class AccountManagerService
return SIGNATURE_CHECK_UID_MATCH;
}
if (pmi.hasSignatureCapability(
- serviceInfo.uid, callingUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
return SIGNATURE_CHECK_MATCH;
}
}
@@ -5668,8 +5671,7 @@ public class AccountManagerService
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
serviceInfos) {
if (isOtherwisePermitted || pmi.hasSignatureCapability(
- serviceInfo.uid, callingUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
managedAccountTypes.add(serviceInfo.type.type);
}
}
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index 8dcc04a27af6..df1b4f432cd6 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -1,9 +1 @@
-carlosvaldivia@google.com
-dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
-yamasani@google.com
-omakoto@google.com
-
+include /core/java/android/accounts/OWNERS
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 99ae52c00995..60b5ab0ff7ec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3290,13 +3290,19 @@ public class ActivityManagerService extends IActivityManager.Stub
final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
final long now = System.currentTimeMillis();
- Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
- for (int i = 0; i < files.length; ++i) {
- if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
- if (!files[i].delete()) {
- Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ try {
+ Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+ for (int i = 0; i < files.length; ++i) {
+ if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+ if (!files[i].delete()) {
+ Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ }
}
}
+ } catch (IllegalArgumentException e) {
+ // The modification times changed while we were sorting. Bail...
+ // https://issuetracker.google.com/169836837
+ Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
}
}
@@ -3452,30 +3458,39 @@ public class ActivityManagerService extends IActivityManager.Stub
final long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
+ boolean permitted = true;
// Instant packages are not protected
if (getPackageManagerInternal().isPackageDataProtected(
resolvedUserId, packageName)) {
- throw new SecurityException(
- "Cannot clear data for a protected package: " + packageName);
+ if (ActivityManager.checkUidPermission(android.Manifest.permission.MANAGE_USERS,
+ uid) == PERMISSION_GRANTED) {
+ // The caller has the MANAGE_USERS permission, tell them what's going on.
+ throw new SecurityException(
+ "Cannot clear data for a protected package: " + packageName);
+ } else {
+ permitted = false; // fall through and throw the SecurityException below.
+ }
}
ApplicationInfo applicationInfo = null;
- try {
- applicationInfo = pm.getApplicationInfo(packageName,
- MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
- } catch (RemoteException e) {
- /* ignore */
+ if (permitted) {
+ try {
+ applicationInfo = pm.getApplicationInfo(packageName,
+ MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ permitted = (applicationInfo != null && applicationInfo.uid == uid) // own uid data
+ || (checkComponentPermission(permission.CLEAR_APP_USER_DATA,
+ pid, uid, -1, true) == PackageManager.PERMISSION_GRANTED);
}
- appInfo = applicationInfo;
-
- final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
- if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
- pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ if (!permitted) {
throw new SecurityException("PID " + pid + " does not have permission "
+ android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
+ " of package " + packageName);
}
+ appInfo = applicationInfo;
final boolean hasInstantMetadata = getPackageManagerInternal()
.hasInstantApplicationMetadata(packageName, resolvedUserId);
@@ -8332,11 +8347,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (lines > 0) {
sb.append("\n");
- // Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
- "/system/bin/timeout", "-k", "15s", "10s",
+ // Time out after 10s, but kill logcat with SEGV
+ // so we can investigate why it didn't finish.
+ "/system/bin/timeout", "-s", "SEGV", "10s",
+ // Merge several logcat streams, and take the last N lines.
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
@@ -12075,6 +12092,31 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, "Unable to bind backup agent for " + packageName);
return false;
}
+ if (app.backupAgentName != null) {
+ final ComponentName backupAgentName = new ComponentName(
+ app.packageName, app.backupAgentName);
+ int enableState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ try {
+ enableState = pm.getComponentEnabledSetting(backupAgentName, instantiatedUserId);
+ } catch (RemoteException e) {
+ // can't happen; package manager is process-local
+ }
+ switch (enableState) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ Slog.w(TAG, "Unable to bind backup agent for " + backupAgentName
+ + ", the backup agent component is disabled.");
+ return false;
+
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ default:
+ // Since there's no way to declare a backup agent disabled in the manifest,
+ // assume the case COMPONENT_ENABLED_STATE_DEFAULT to be enabled.
+ break;
+ }
+ }
int oldBackupUid;
int newBackupUid;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9f41c8ba9626..399a245a6fc3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -492,9 +492,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
powerMgr.registerLowPowerModeObserver(this);
synchronized (mStats) {
- mStats.notePowerSaveModeLocked(
+ mStats.notePowerSaveModeLockedInit(
powerMgr.getLowPowerState(ServiceType.BATTERY_STATS).batterySaverEnabled,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), true);
+ SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
}
(new WakeupReasonThread()).start();
}
@@ -537,7 +537,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler.post(() -> {
synchronized (mStats) {
mStats.notePowerSaveModeLocked(result.batterySaverEnabled,
- elapsedRealtime, uptime, false);
+ elapsedRealtime, uptime);
}
});
}
@@ -1978,7 +1978,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
@Override
- public void noteResetBleScan() {
+ public void noteBleScanReset() {
enforceCallingPermission();
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 50278fd81bb2..1ead7e3c589f 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -38,16 +38,25 @@ public class CacheOomRanker {
private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
- @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+ @VisibleForTesting
+ static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+ @VisibleForTesting
+ static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
+ "oom_re_ranking_preserve_top_n_apps";
+ @VisibleForTesting
+ static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
new ScoreComparator();
@@ -66,15 +75,21 @@ public class CacheOomRanker {
@GuardedBy("mPhenotypeFlagLock")
private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting
+ int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
// Weight to apply to the LRU ordering.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
+ @VisibleForTesting
+ float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
// Weight to apply to the ordering by number of times the process has been added to the cache.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
+ @VisibleForTesting
+ float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
// Weight to apply to the ordering by RSS used by the processes.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
+ @VisibleForTesting
+ float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
// Positions to replace in the lru list.
@GuardedBy("mPhenotypeFlagLock")
@@ -93,6 +108,8 @@ public class CacheOomRanker {
updateUseOomReranking();
} else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
updateNumberToReRank();
+ } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
+ updatePreserveTopNApps();
} else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
updateLruWeight();
} else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -160,6 +177,19 @@ public class CacheOomRanker {
}
@GuardedBy("mPhenotypeFlagLock")
+ private void updatePreserveTopNApps() {
+ int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
+ if (preserveTopNApps < 0) {
+ Slog.w(OomAdjuster.TAG,
+ "Found negative value for preserveTopNApps, setting to default: "
+ + preserveTopNApps);
+ preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+ }
+ mPreserveTopNApps = preserveTopNApps;
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
private void updateLruWeight() {
mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
@@ -183,6 +213,33 @@ public class CacheOomRanker {
*/
@GuardedBy({"mService", "mProcLock"})
void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
+ // The lruList is a list of processes ordered by how recently they were used. The
+ // least-recently-used apps are at the beginning of the list. We keep track of two
+ // indices in the lruList:
+ //
+ // getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
+ // lruList=
+ // 0: app A ^
+ // 1: app B | These apps are re-ranked, as they are the first five apps (see
+ // 2: app C | getNumberToReRank), excluding...
+ // 3: app D v
+ // 4: app E ^
+ // 5: app F | The three most-recently-used apps in the cache (see preserveTopNApps).
+ // 6: app G v
+ // 7: service A ^
+ // 8: service B | Everything beyond lruProcessServiceStart is ignored, as these aren't
+ // 9: service C | apps.
+ // 10: activity A |
+ // ... |
+ //
+ // `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
+ // apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
+ // Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
+ // happens.
+ //
+ // Note that some apps in the `lruList` can be skipped, if they don't pass
+ //`appCanBeReRanked`.
+
float lruWeight;
float usesWeight;
float rssWeight;
@@ -202,52 +259,67 @@ public class CacheOomRanker {
return;
}
+ int numProcessesEvaluated = 0;
// Collect the least recently used processes to re-rank, only rank cached
// processes further down the list than mLruProcessServiceStart.
- int cachedProcessPos = 0;
- for (int i = 0; i < lruProcessServiceStart
- && cachedProcessPos < scoredProcessRecords.length; ++i) {
- ProcessRecord app = lruList.get(i);
+ int numProcessesReRanked = 0;
+ while (numProcessesEvaluated < lruProcessServiceStart
+ && numProcessesReRanked < scoredProcessRecords.length) {
+ ProcessRecord process = lruList.get(numProcessesEvaluated);
// Processes that will be assigned a cached oom adj score.
- if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
- >= ProcessList.UNKNOWN_ADJ) {
- scoredProcessRecords[cachedProcessPos].proc = app;
- scoredProcessRecords[cachedProcessPos].score = 0.0f;
- lruPositions[cachedProcessPos] = i;
- ++cachedProcessPos;
+ if (appCanBeReRanked(process)) {
+ scoredProcessRecords[numProcessesReRanked].proc = process;
+ scoredProcessRecords[numProcessesReRanked].score = 0.0f;
+ lruPositions[numProcessesReRanked] = numProcessesEvaluated;
+ ++numProcessesReRanked;
}
+ ++numProcessesEvaluated;
}
- // TODO maybe ensure a certain number above this in the cache before re-ranking.
- if (cachedProcessPos < scoredProcessRecords.length) {
- // Ignore we don't have enough processes to worry about re-ranking.
- return;
+ // Count how many apps we're not re-ranking (up to mPreserveTopNApps).
+ int numProcessesNotReRanked = 0;
+ while (numProcessesEvaluated < lruProcessServiceStart
+ && numProcessesNotReRanked < mPreserveTopNApps) {
+ ProcessRecord process = lruList.get(numProcessesEvaluated);
+ if (appCanBeReRanked(process)) {
+ numProcessesNotReRanked++;
+ }
+ numProcessesEvaluated++;
+ }
+ // Exclude the top `mPreserveTopNApps` apps from re-ranking.
+ if (numProcessesNotReRanked < mPreserveTopNApps) {
+ numProcessesReRanked -= mPreserveTopNApps - numProcessesNotReRanked;
+ if (numProcessesReRanked < 0) {
+ numProcessesReRanked = 0;
+ }
}
// Add scores for each of the weighted features we want to rank based on.
if (lruWeight > 0.0f) {
// This doesn't use the LRU list ordering as after the first re-ranking
// that will no longer be lru.
- Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+ LAST_ACTIVITY_TIME_COMPARATOR);
addToScore(scoredProcessRecords, lruWeight);
}
if (rssWeight > 0.0f) {
synchronized (mService.mAppProfiler.mProfilerLock) {
- Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
}
addToScore(scoredProcessRecords, rssWeight);
}
if (usesWeight > 0.0f) {
- Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
addToScore(scoredProcessRecords, usesWeight);
}
// Re-rank by the new combined score.
- Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+ SCORED_PROCESS_RECORD_COMPARATOR);
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
boolean printedHeader = false;
- for (int i = 0; i < scoredProcessRecords.length; ++i) {
+ for (int i = 0; i < numProcessesReRanked; ++i) {
if (scoredProcessRecords[i].proc.getPid()
!= lruList.get(lruPositions[i]).getPid()) {
if (!printedHeader) {
@@ -260,12 +332,18 @@ public class CacheOomRanker {
}
}
- for (int i = 0; i < scoredProcessRecords.length; ++i) {
+ for (int i = 0; i < numProcessesReRanked; ++i) {
lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
scoredProcessRecords[i].proc = null;
}
}
+ private static boolean appCanBeReRanked(ProcessRecord process) {
+ return !process.isKilledByAm()
+ && process.getThread() != null
+ && process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
+ }
+
private static void addToScore(RankedProcessRecord[] scores, float weight) {
for (int i = 1; i < scores.length; ++i) {
scores[i].score += i * weight;
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 5c9d38515e49..2e3e635c1157 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -94,8 +94,6 @@ final class CoreSettingsObserver extends ContentObserver {
sGlobalSettingToTypeMap.put(
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
sGlobalSettingToTypeMap.put(
- Settings.Global.ANGLE_ALLOWLIST, String.class);
- sGlobalSettingToTypeMap.put(
Settings.Global.ANGLE_EGL_FEATURES, String.class);
sGlobalSettingToTypeMap.put(
Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d6bf8dbe0a6a..93d5c06b40c9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -256,7 +256,7 @@ public class OomAdjuster {
@GuardedBy("mService")
private boolean mPendingFullOomAdjUpdate = false;
- private final PlatformCompatCache mPlatformCompatCache;
+ final PlatformCompatCache mPlatformCompatCache;
/** Overrideable by a test */
@VisibleForTesting
@@ -309,6 +309,12 @@ public class OomAdjuster {
}
}
+ void onApplicationInfoChanged(ApplicationInfo app) {
+ for (int i = mCaches.size() - 1; i >= 0; i--) {
+ mCaches.valueAt(i).onApplicationInfoChanged(app);
+ }
+ }
+
static class CacheItem implements CompatChange.ChangeListener {
private final PlatformCompat mPlatformCompat;
private final long mChangeId;
@@ -328,22 +334,14 @@ public class OomAdjuster {
final int index = mCache.indexOfKey(app.packageName);
Pair<Boolean, WeakReference<ApplicationInfo>> p;
if (index < 0) {
- p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
- app),
- new WeakReference<>(app));
- mCache.put(app.packageName, p);
- return p.first;
+ return fetchLocked(app, index);
}
p = mCache.valueAt(index);
if (p.second.get() == app) {
return p.first;
}
// Cache is invalid, regenerate it
- p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
- app),
- new WeakReference<>(app));
- mCache.setValueAt(index, p);
- return p.first;
+ return fetchLocked(app, index);
}
}
@@ -353,10 +351,40 @@ public class OomAdjuster {
}
}
+ @GuardedBy("mLock")
+ boolean fetchLocked(ApplicationInfo app, int index) {
+ final Pair<Boolean, WeakReference<ApplicationInfo>> p = new Pair<>(
+ mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId, app),
+ new WeakReference<>(app));
+ if (index >= 0) {
+ mCache.setValueAt(index, p);
+ } else {
+ mCache.put(app.packageName, p);
+ }
+ return p.first;
+ }
+
+ void onApplicationInfoChanged(ApplicationInfo app) {
+ synchronized (mLock) {
+ final int index = mCache.indexOfKey(app.packageName);
+ if (index >= 0) {
+ fetchLocked(app, index);
+ }
+ }
+ }
+
@Override
public void onCompatChange(String packageName) {
synchronized (mLock) {
- mCache.remove(packageName);
+ final int index = mCache.indexOfKey(packageName);
+ if (index >= 0) {
+ final ApplicationInfo app = mCache.valueAt(index).second.get();
+ if (app != null) {
+ fetchLocked(app, index);
+ } else {
+ mCache.removeAt(index);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b77270f5963b..a97738f964be 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4710,6 +4710,8 @@ public final class ProcessList {
if (ai != null) {
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
+ mService.mOomAdjuster.mPlatformCompatCache
+ .onApplicationInfoChanged(ai);
}
app.getThread().scheduleApplicationInfoChanged(ai);
targetProcesses.add(app.getWindowProcessController());
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e022e977e02f..c10334470ae1 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -92,6 +92,8 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_STATSD_NATIVE,
DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ba3e1fb95e7d..fa7eae3b2231 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -425,6 +425,7 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
+ @VisibleForTesting
List<Integer> getRunningUsersLU() {
ArrayList<Integer> runningUsers = new ArrayList<>();
for (Integer userId : mUserLru) {
@@ -450,7 +451,7 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
- void stopRunningUsersLU(int maxRunningUsers) {
+ private void stopRunningUsersLU(int maxRunningUsers) {
List<Integer> currentlyRunning = getRunningUsersLU();
Iterator<Integer> iterator = currentlyRunning.iterator();
while (currentlyRunning.size() > maxRunningUsers && iterator.hasNext()) {
@@ -590,7 +591,11 @@ class UserController implements Handler.Callback {
Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
return;
}
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("UM.onBeforeUnlockUser-" + userId);
mInjector.getUserManager().onBeforeUnlockUser(userId);
+ t.traceEnd();
synchronized (mLock) {
// Do not proceed if unexpected state
if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
@@ -612,7 +617,7 @@ class UserController implements Handler.Callback {
* Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
* {@link UserState#STATE_RUNNING_UNLOCKED}.
*/
- void finishUserUnlocked(final UserState uss) {
+ private void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED, userId);
// Only keep marching forward if user is actually unlocked
@@ -907,7 +912,7 @@ class UserController implements Handler.Callback {
private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback,
KeyEvictedCallback keyEvictedCallback) {
- if (DEBUG_MU) Slogf.i(TAG, "stopSingleUserLocked userId=" + userId);
+ Slogf.i(TAG, "stopSingleUserLU userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) { // User is not started
// If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
@@ -1000,7 +1005,7 @@ class UserController implements Handler.Callback {
}
}
- void finishUserStopping(final int userId, final UserState uss,
+ private void finishUserStopping(final int userId, final UserState uss,
final boolean allowDelayedLocking) {
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
synchronized (mLock) {
@@ -1041,6 +1046,7 @@ class UserController implements Handler.Callback {
Binder.getCallingPid(), userId);
}
+ @VisibleForTesting
void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
final int userId = uss.mHandle.getIdentifier();
if (DEBUG_MU) {
@@ -1264,7 +1270,7 @@ class UserController implements Handler.Callback {
});
}
- void startProfiles() {
+ private void startProfiles() {
int currentUserId = getCurrentUserId();
if (DEBUG_MU) Slogf.i(TAG, "startProfilesLocked");
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
@@ -1317,6 +1323,7 @@ class UserController implements Handler.Callback {
return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null);
}
+ @VisibleForTesting
boolean startUser(final @UserIdInt int userId, final boolean foreground) {
return startUser(userId, foreground, null);
}
@@ -1683,7 +1690,11 @@ class UserController implements Handler.Callback {
return false;
}
- if (!finishUserUnlocking(uss)) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("finishUserUnlocking-" + userId);
+ final boolean finishUserUnlockingResult = finishUserUnlocking(uss);
+ t.traceEnd();
+ if (!finishUserUnlockingResult) {
notifyFinished(userId, listener);
return false;
}
@@ -1775,6 +1786,7 @@ class UserController implements Handler.Callback {
}
/** Called on handler thread */
+ @VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int userId) {
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
@@ -1848,7 +1860,11 @@ class UserController implements Handler.Callback {
}
}
+ @VisibleForTesting
void dispatchUserSwitch(final UserState uss, final int oldUserId, final int newUserId) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
+
EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
final int observerCount = mUserSwitchObservers.beginBroadcast();
@@ -1901,27 +1917,36 @@ class UserController implements Handler.Callback {
}
}
mUserSwitchObservers.finishBroadcast();
+ t.traceEnd(); // end dispatchUserSwitch-
}
@GuardedBy("mLock")
- void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
+ private void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
}
+ @VisibleForTesting
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("continueUserSwitch-" + oldUserId + "-to-" + newUserId);
+
EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId);
if (isUserSwitchUiEnabled()) {
+ t.traceBegin("stopFreezingScreen");
mInjector.getWindowManager().stopFreezingScreen();
+ t.traceEnd();
}
uss.switching = false;
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
stopGuestOrEphemeralUserIfBackground(oldUserId);
stopBackgroundUsersOnSwitchIfEnforced(oldUserId);
+
+ t.traceEnd(); // end continueUserSwitch
}
private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
@@ -2354,7 +2379,7 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
- UserInfo getCurrentUserLU() {
+ private UserInfo getCurrentUserLU() {
int userId = getCurrentOrTargetUserIdLU();
return getUserInfo(userId);
}
@@ -2366,12 +2391,12 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
- int getCurrentOrTargetUserIdLU() {
+ private int getCurrentOrTargetUserIdLU() {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
@GuardedBy("mLock")
- int getCurrentUserIdLU() {
+ private int getCurrentUserIdLU() {
return mCurrentUserId;
}
@@ -2670,7 +2695,11 @@ class UserController implements Handler.Callback {
USER_LIFECYCLE_EVENT_STATE_FINISH);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("finishUserUnlocked-" + userId);
finishUserUnlocked((UserState) msg.obj);
+ t.traceEnd();
break;
case USER_UNLOCKED_MSG:
mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index af8d7a6a282b..3003c520254b 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -272,6 +272,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
"com.android.graphics.intervention.wm.allowDownscale";
/**
+ * Metadata that can be included in the app manifest to allow/disallow any ANGLE
+ * interventions. Default value is TRUE.
+ */
+ public static final String METADATA_ANGLE_ALLOW_ANGLE =
+ "com.android.graphics.intervention.angle.allowAngle";
+
+ /**
* Metadata that needs to be included in the app manifest to OPT-IN to PERFORMANCE mode.
* This means the app will assume full responsibility for the experience provided by this
* mode and the system will enable no window manager downscaling.
@@ -294,6 +301,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
private boolean mPerfModeOptedIn;
private boolean mBatteryModeOptedIn;
private boolean mAllowDownscale;
+ private boolean mAllowAngle;
GamePackageConfiguration(String packageName, int userId) {
mPackageName = packageName;
@@ -305,10 +313,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
+ mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
} else {
mPerfModeOptedIn = false;
mBatteryModeOptedIn = false;
mAllowDownscale = true;
+ mAllowAngle = true;
}
} catch (PackageManager.NameNotFoundException e) {
// Not all packages are installed, hence ignore those that are not installed yet.
@@ -340,14 +350,26 @@ public final class GameManagerService extends IGameManagerService.Stub {
public static final String MODE_KEY = "mode";
public static final String SCALING_KEY = "downscaleFactor";
public static final String DEFAULT_SCALING = "1.0";
+ public static final String ANGLE_KEY = "useAngle";
private final @GameMode int mGameMode;
private final String mScaling;
+ private final boolean mUseAngle;
GameModeConfiguration(KeyValueListParser parser) {
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
- mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode)
+ // isGameModeOptedIn() returns if an app will handle all of the changes necessary
+ // for a particular game mode. If so, the Android framework (i.e.
+ // GameManagerService) will not do anything for the app (like window scaling or
+ // using ANGLE).
+ mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+ // We only want to use ANGLE if:
+ // - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
+ // - The app has not opted in to performing the work itself AND
+ // - The Phenotype config has enabled it.
+ mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
+ && parser.getBoolean(ANGLE_KEY, false);
}
public int getGameMode() {
@@ -358,6 +380,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
return mScaling;
}
+ public boolean getUseAngle() {
+ return mUseAngle;
+ }
+
public boolean isValid() {
return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
|| mGameMode == GameManager.GAME_MODE_BATTERY)
@@ -368,7 +394,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
* @hide
*/
public String toString() {
- return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
+ return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:"
+ + mUseAngle + "]";
}
/**
@@ -384,13 +411,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
- * Gets whether a package has opted into a game mode via its manifest.
+ * Returns if the app will assume full responsibility for the experience provided by this
+ * mode. If True, the system will not perform any interventions for the app.
*
* @return True if the app package has specified in its metadata either:
* "com.android.app.gamemode.performance.enabled" or
* "com.android.app.gamemode.battery.enabled" with a value of "true"
*/
- public boolean isGameModeOptedIn(@GameMode int gameMode) {
+ public boolean willGamePerformOptimizations(@GameMode int gameMode) {
return (mBatteryModeOptedIn && gameMode == GameManager.GAME_MODE_BATTERY)
|| (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE);
}
@@ -631,7 +659,34 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
- updateCompatModeDownscale(packageName, gameMode);
+ updateInterventions(packageName, gameMode);
+ }
+
+ /**
+ * Get if ANGLE is enabled for the package for the currently enabled game mode.
+ * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @GameMode boolean getAngleEnabled(String packageName, int userId)
+ throws SecurityException {
+ final int gameMode = getGameMode(packageName, userId);
+ if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+ return false;
+ }
+
+ synchronized (mDeviceConfigLock) {
+ final GamePackageConfiguration config = mConfigs.get(packageName);
+ if (config == null) {
+ return false;
+ }
+ GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ config.getGameModeConfiguration(gameMode);
+ if (gameModeConfiguration == null) {
+ return false;
+ }
+ return gameModeConfiguration.getUseAngle();
+ }
}
/**
@@ -753,7 +808,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (DEBUG) {
Slog.v(TAG, dumpDeviceConfigs());
}
- if (packageConfig.isGameModeOptedIn(gameMode)) {
+ if (packageConfig.willGamePerformOptimizations(gameMode)) {
disableCompatScale(packageName);
return;
}
@@ -782,6 +837,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
return (bitField & modeToBitmask(gameMode)) != 0;
}
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ private void updateUseAngle(String packageName, @GameMode int gameMode) {
+ // TODO (b/188475576): Nothing to do yet. Remove if it's still empty when we're ready to
+ // ship.
+ }
+
+ private void updateInterventions(String packageName, @GameMode int gameMode) {
+ updateCompatModeDownscale(packageName, gameMode);
+ updateUseAngle(packageName, gameMode);
+ }
+
/**
* @hide
*/
@@ -839,11 +905,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (newGameMode != gameMode) {
setGameMode(packageName, newGameMode, userId);
}
- updateCompatModeDownscale(packageName, gameMode);
+ updateInterventions(packageName, gameMode);
}
}
} catch (Exception e) {
- Slog.e(TAG, "Failed to update compat modes for user: " + userId);
+ Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
}
}
@@ -851,7 +917,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
final List<PackageInfo> packages =
mPackageManager.getInstalledPackagesAsUser(0, userId);
return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category
- == ApplicationInfo.CATEGORY_GAME)
+ == ApplicationInfo.CATEGORY_GAME)
.map(e -> e.packageName)
.toArray(String[]::new);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 91283b9c6ccc..fcd198e8831c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -64,6 +64,7 @@ import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
+import android.hardware.hdmi.HdmiClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiTvClient;
@@ -585,11 +586,12 @@ public class AudioService extends IAudioService.Stub
Set<Integer> mFixedVolumeDevices = new HashSet<>(Arrays.asList(
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
- AudioSystem.DEVICE_OUT_HDMI_ARC,
- AudioSystem.DEVICE_OUT_HDMI_EARC,
AudioSystem.DEVICE_OUT_AUX_LINE));
// Devices for which the volume is always max, no volume panel
- Set<Integer> mFullVolumeDevices = new HashSet<>();
+ Set<Integer> mFullVolumeDevices = new HashSet<>(Arrays.asList(
+ AudioSystem.DEVICE_OUT_HDMI_ARC,
+ AudioSystem.DEVICE_OUT_HDMI_EARC
+ ));
// Devices for the which use the "absolute volume" concept (framework sends audio signal
// full scale, and volume control separately) and can be used for multiple use cases reflected
// by the audio mode (e.g. media playback in MODE_NORMAL, and phone calls in MODE_IN_CALL).
@@ -1930,6 +1932,32 @@ public class AudioService extends IAudioService.Stub
}
}
+ /** @see AudioManager#getSurroundFormats() */
+ @Override
+ public Map<Integer, Boolean> getSurroundFormats() {
+ Map<Integer, Boolean> surroundFormats = new HashMap<>();
+ int status = AudioSystem.getSurroundFormats(surroundFormats);
+ if (status != AudioManager.SUCCESS) {
+ // fail and bail!
+ Log.e(TAG, "getSurroundFormats failed:" + status);
+ return new HashMap<>(); // Always return a map.
+ }
+ return surroundFormats;
+ }
+
+ /** @see AudioManager#getReportedSurroundFormats() */
+ @Override
+ public List<Integer> getReportedSurroundFormats() {
+ ArrayList<Integer> reportedSurroundFormats = new ArrayList<>();
+ int status = AudioSystem.getReportedSurroundFormats(reportedSurroundFormats);
+ if (status != AudioManager.SUCCESS) {
+ // fail and bail!
+ Log.e(TAG, "getReportedSurroundFormats failed:" + status);
+ return new ArrayList<>(); // Always return a list.
+ }
+ return reportedSurroundFormats;
+ }
+
/** @see AudioManager#isSurroundFormatEnabled(int) */
@Override
public boolean isSurroundFormatEnabled(int audioFormat) {
@@ -2280,7 +2308,6 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
}
- setSystemAudioMute(masterMute);
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
@@ -2582,14 +2609,6 @@ public class AudioService extends IAudioService.Stub
}
}
- /** @see AudioManager#adjustVolume(int, int) */
- public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
- String callingPackage, String caller) {
- adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
- caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
- VOL_ADJUST_NORMAL);
- }
-
public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
mNavigationRepeatSoundEffectsEnabled = enabled;
}
@@ -2612,6 +2631,7 @@ public class AudioService extends IAudioService.Stub
return mHomeSoundEffectEnabled;
}
+ /** All callers come from platform apps/system server, so no attribution tag is needed */
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
int keyEventMode) {
@@ -2687,7 +2707,7 @@ public class AudioService extends IAudioService.Stub
}
adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
- hasModifyAudioSettings, keyEventMode);
+ null, hasModifyAudioSettings, keyEventMode);
}
private boolean notifyExternalVolumeController(int direction) {
@@ -2705,10 +2725,16 @@ public class AudioService extends IAudioService.Stub
return true;
}
- /** @see AudioManager#adjustStreamVolume(int, int, int)
- * Part of service interface, check permissions here */
+ /** Retain API for unsupported app usage */
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
+ adjustStreamVolumeWithAttribution(streamType, direction, flags, callingPackage, null);
+ }
+
+ /** @see AudioManager#adjustStreamVolume(int, int, int)
+ * Part of service interface, check permissions here */
+ public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
+ String callingPackage, String attributionTag) {
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -2718,13 +2744,13 @@ public class AudioService extends IAudioService.Stub
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+ Binder.getCallingUid(), attributionTag, callingHasAudioSettingsPermission(),
VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
- String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
- int keyEventMode) {
+ String callingPackage, String caller, int uid, String attributionTag,
+ boolean hasModifyAudioSettings, int keyEventMode) {
if (mUseFixedVolume) {
return;
}
@@ -2789,8 +2815,8 @@ public class AudioService extends IAudioService.Stub
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -2862,9 +2888,6 @@ public class AudioService extends IAudioService.Stub
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioMute(state);
- }
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (streamTypeAlias == mStreamVolumeAlias[stream]) {
if (!(readCameraSoundForced()
@@ -2930,11 +2953,6 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
}
}
-
- // Check if volume update should be sent to Hdmi system audio.
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
- }
}
final int newIndex = mStreamStates[streamType].getIndex(device);
@@ -2942,7 +2960,13 @@ public class AudioService extends IAudioService.Stub
if (adjustVolume) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- if (mHdmiPlaybackClient != null
+ // At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-null
+ HdmiClient fullVolumeHdmiClient = mHdmiPlaybackClient;
+ if (mHdmiTvClient != null) {
+ fullVolumeHdmiClient = mHdmiTvClient;
+ }
+
+ if (fullVolumeHdmiClient != null
&& mHdmiCecVolumeControlEnabled
&& streamTypeAlias == AudioSystem.STREAM_MUSIC
// vol change on a full volume device
@@ -2956,6 +2980,10 @@ public class AudioService extends IAudioService.Stub
keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
break;
case AudioManager.ADJUST_TOGGLE_MUTE:
+ case AudioManager.ADJUST_MUTE:
+ case AudioManager.ADJUST_UNMUTE:
+ // Many CEC devices only support toggle mute. Therefore, we send the
+ // same keycode for all three mute options.
keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
break;
default:
@@ -2964,17 +2992,16 @@ public class AudioService extends IAudioService.Stub
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
final long ident = Binder.clearCallingIdentity();
try {
- final long time = java.lang.System.currentTimeMillis();
switch (keyEventMode) {
case VOL_ADJUST_NORMAL:
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
break;
case VOL_ADJUST_START:
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
break;
case VOL_ADJUST_END:
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
break;
default:
Log.e(TAG, "Invalid keyEventMode " + keyEventMode);
@@ -3029,27 +3056,6 @@ public class AudioService extends IAudioService.Stub
Binder.restoreCallingIdentity(identity);
}
- private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
- // Sets the audio volume of AVR when we are in system audio mode. The new volume info
- // is tranformed to HDMI-CEC commands and passed through CEC bus.
- synchronized (mHdmiClientLock) {
- if (mHdmiManager == null
- || mHdmiTvClient == null
- || oldVolume == newVolume
- || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0
- || !mHdmiSystemAudioSupported
- || !mHdmiCecVolumeControlEnabled) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
// StreamVolumeCommand contains the information needed to defer the process of
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
static class StreamVolumeCommand {
@@ -3156,7 +3162,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
- String callingPackage) {
+ String callingPackage, String attributionTag) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(attr, "attr must not be null");
final int volumeGroup = getVolumeGroupIdForAttributes(attr);
@@ -3181,7 +3187,7 @@ public class AudioService extends IAudioService.Stub
continue;
}
setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), true /*hasModifyAudioSettings*/);
+ attributionTag, Binder.getCallingUid(), true /*hasModifyAudioSettings*/);
}
}
@@ -3223,9 +3229,15 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.getMinVolumeIndexForAttributes(attr);
}
+ /** Retain API for unsupported app usage */
+ public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+ setStreamVolumeWithAttribution(streamType, index, flags, callingPackage, null);
+ }
+
/** @see AudioManager#setStreamVolume(int, int, int)
* Part of service interface, check permissions here */
- public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+ public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
+ String callingPackage, String attributionTag) {
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
@@ -3251,7 +3263,7 @@ public class AudioService extends IAudioService.Stub
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
+ attributionTag, Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
}
private boolean canChangeAccessibilityVolume() {
@@ -3486,7 +3498,8 @@ public class AudioService extends IAudioService.Stub
}
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
- String caller, int uid, boolean hasModifyAudioSettings) {
+ String caller, String attributionTag, int uid,
+ boolean hasModifyAudioSettings) {
if (DEBUG_VOL) {
Log.d(TAG, "setStreamVolume(stream=" + streamType+", index=" + index
+ ", calling=" + callingPackage + ")");
@@ -3513,8 +3526,8 @@ public class AudioService extends IAudioService.Stub
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -3553,10 +3566,6 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
}
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags);
- }
-
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
@@ -3600,7 +3609,7 @@ public class AudioService extends IAudioService.Stub
}
// The default volume group is the one hosted by default product strategy, i.e.
// supporting Default Attributes
- return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
+ return getVolumeGroupIdForAttributesInt(AudioProductStrategy.getDefaultAttributes());
}
private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
@@ -3818,18 +3827,6 @@ public class AudioService extends IAudioService.Stub
}
}
- private void setSystemAudioMute(boolean state) {
- synchronized (mHdmiClientLock) {
- if (mHdmiManager == null || mHdmiTvClient == null || !mHdmiSystemAudioSupported) return;
- final long token = Binder.clearCallingIdentity();
- try {
- mHdmiTvClient.setSystemAudioMute(state);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
/** get stream mute state. */
public boolean isStreamMute(int streamType) {
if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -3942,15 +3939,15 @@ public class AudioService extends IAudioService.Stub
}
private void setMasterMuteInternal(boolean mute, int flags, String callingPackage, int uid,
- int userId) {
+ int userId, String attributionTag) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
// If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
- if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
if (userId != UserHandle.getCallingUserId() &&
@@ -3980,7 +3977,6 @@ public class AudioService extends IAudioService.Stub
if ((isPlatformAutomotive() && userId == UserHandle.USER_SYSTEM)
|| (getCurrentUserId() == userId)) {
if (mute != AudioSystem.getMasterMute()) {
- setSystemAudioMute(mute);
AudioSystem.setMasterMute(mute);
sendMasterMuteUpdate(mute, flags);
}
@@ -3992,10 +3988,12 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.getMasterMute();
}
- public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) {
+ /** @see AudioManager#setMasterMute(boolean, int) */
+ public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
+ String attributionTag) {
enforceModifyAudioRoutingPermission();
- setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(),
- userId);
+ setMasterMuteInternal(mute, flags, callingPackage,
+ Binder.getCallingUid(), userId, attributionTag);
}
/** @see AudioManager#getStreamVolume(int) */
@@ -4066,7 +4064,8 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#setMicrophoneMute(boolean) */
@Override
- public void setMicrophoneMute(boolean on, String callingPackage, int userId) {
+ public void setMicrophoneMute(boolean on, String callingPackage, int userId,
+ String attributionTag) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
int uid = Binder.getCallingUid();
@@ -4081,8 +4080,8 @@ public class AudioService extends IAudioService.Stub
? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE);
// If OP_MUTE_MICROPHONE is set, disallow unmuting.
- if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record();
return;
}
@@ -4918,7 +4917,7 @@ public class AudioService extends IAudioService.Stub
}
adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
- hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
+ null, hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4930,7 +4929,7 @@ public class AudioService extends IAudioService.Stub
throw new SecurityException("Should only be called from system process");
}
- setStreamVolume(streamType, index, flags, packageName, packageName, uid,
+ setStreamVolume(streamType, index, flags, packageName, packageName, null, uid,
hasAudioSettingsPermission(uid, pid));
}
@@ -6340,7 +6339,7 @@ public class AudioService extends IAudioService.Stub
private void ensureValidAttributes(AudioVolumeGroup avg) {
boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
- .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+ .anyMatch(aa -> !aa.equals(AudioProductStrategy.getDefaultAttributes()));
if (!hasAtLeastOneValidAudioAttributes) {
throw new IllegalArgumentException("Volume Group " + avg.name()
+ " has no valid audio attributes");
@@ -6388,7 +6387,7 @@ public class AudioService extends IAudioService.Stub
private int mIndexMax;
private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
- private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+ private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
// No API in AudioSystem to get a device from strategy or from attributes.
// Need a valid public stream type to use current API getDeviceForStream
@@ -6401,8 +6400,9 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.v(TAG, "VolumeGroupState for " + avg.toString());
}
+ // mAudioAttributes is the default at this point
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(mAudioAttributes)) {
mAudioAttributes = aa;
break;
}
@@ -8052,8 +8052,8 @@ public class AudioService extends IAudioService.Stub
}
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
- IAudioPolicyCallback pcb, int sdk) {
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName,
+ String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
final int uid = Binder.getCallingUid();
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
.setUid(uid)
@@ -8105,7 +8105,7 @@ public class AudioService extends IAudioService.Stub
}
mmi.record();
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, flags, sdk,
+ clientId, callingPackageName, attributionTag, flags, sdk,
forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/);
}
@@ -8122,7 +8122,7 @@ public class AudioService extends IAudioService.Stub
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, AudioManager.AUDIOFOCUS_FLAG_TEST,
+ clientId, callingPackageName, null, AudioManager.AUDIOFOCUS_FLAG_TEST,
sdk, false /*forceDuck*/, fakeUid);
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index e6c4abfa2086..6fe129527b2d 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -857,6 +857,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* @param fd
* @param clientId
* @param callingPackageName
+ * @param attributionTag
* @param flags
* @param sdk
* @param forceDuck only true if
@@ -868,7 +869,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
*/
protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
- int flags, int sdk, boolean forceDuck, int testUid) {
+ String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) {
new MediaMetrics.Item(mMetricsId)
.setUid(Binder.getCallingUid())
.set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
@@ -903,7 +904,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST)
// note we're using the real uid for appOp evaluation
&& (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
- callingPackageName) != AppOpsManager.MODE_ALLOWED)) {
+ callingPackageName, attributionTag, null) != AppOpsManager.MODE_ALLOWED)) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 012e47e425f6..0e7120f3371c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -263,7 +263,7 @@ public class FingerprintService extends SystemService {
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
// Clear calling identity when checking LockPatternUtils for StrongAuth flags.
- long identity = Binder.clearCallingIdentity();
+ final long identity1 = Binder.clearCallingIdentity();
try {
if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -273,7 +273,7 @@ public class FingerprintService extends SystemService {
return;
}
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(identity1);
}
final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
@@ -297,11 +297,11 @@ public class FingerprintService extends SystemService {
provider.second.getSensorProperties(sensorId);
if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
- identity = Binder.clearCallingIdentity();
+ final long identity2 = Binder.clearCallingIdentity();
try {
authenticateWithPrompt(operationId, sensorProps, userId, receiver);
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(identity2);
}
} else {
provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index b2d35f45ab44..ed7d185a5ef0 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -35,11 +35,14 @@ import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.nfc.INfcAdapter;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
@@ -57,8 +60,8 @@ import android.view.IDisplayWindowListener;
import android.view.Surface;
import android.view.WindowManagerGlobal;
-import com.android.internal.annotations.GuardedBy;
import com.android.framework.protobuf.nano.MessageNano;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -447,6 +450,8 @@ public class CameraServiceProxy extends SystemService
}
};
+ private final FoldStateListener mFoldStateListener;
+
public CameraServiceProxy(Context context) {
super(context);
mContext = context;
@@ -459,6 +464,14 @@ public class CameraServiceProxy extends SystemService
// Don't keep any extra logging threads if not needed
mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS);
mLogWriterService.allowCoreThreadTimeOut(true);
+
+ mFoldStateListener = new FoldStateListener(mContext, folded -> {
+ if (folded) {
+ setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+ } else {
+ clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+ }
+ });
}
/**
@@ -471,7 +484,7 @@ public class CameraServiceProxy extends SystemService
*
* @see #clearDeviceStateFlags(int)
*/
- public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+ private void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
synchronized (mLock) {
mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
mDeviceState |= deviceStateFlags;
@@ -491,7 +504,7 @@ public class CameraServiceProxy extends SystemService
*
* @see #setDeviceStateFlags(int)
*/
- public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+ private void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
synchronized (mLock) {
mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
mDeviceState &= ~deviceStateFlags;
@@ -555,6 +568,9 @@ public class CameraServiceProxy extends SystemService
} catch (RemoteException e) {
Log.e(TAG, "Failed to register display window listener!");
}
+
+ mContext.getSystemService(DeviceStateManager.class)
+ .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
}
}
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
new file mode 100644
index 000000000000..a81213df6fe3
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -0,0 +1,383 @@
+/*
+ * 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.compat.overrides;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A utility class for parsing App Compat Overrides flags.
+ *
+ * @hide
+ */
+final class AppCompatOverridesParser {
+ /**
+ * Flag for specifying all compat change IDs owned by a namespace. See {@link
+ * #parseOwnedChangeIds} for information on how this flag is parsed.
+ */
+ static final String FLAG_OWNED_CHANGE_IDS = "owned_change_ids";
+
+ /**
+ * Flag for immediately removing overrides for certain packages and change IDs (from the compat
+ * platform), as well as stopping to apply them, in case of an emergency. See {@link
+ * #parseRemoveOverrides} for information on how this flag is parsed.
+ */
+ static final String FLAG_REMOVE_OVERRIDES = "remove_overrides";
+
+ private static final String TAG = "AppCompatOverridesParser";
+
+ private static final String WILDCARD_SYMBOL = "*";
+
+ private static final Pattern BOOLEAN_PATTERN =
+ Pattern.compile("true|false", Pattern.CASE_INSENSITIVE);
+
+ private static final String WILDCARD_NO_OWNED_CHANGE_IDS_WARNING =
+ "Wildcard can't be used in '" + FLAG_REMOVE_OVERRIDES + "' flag with an empty "
+ + FLAG_OWNED_CHANGE_IDS + "' flag";
+
+ private final PackageManager mPackageManager;
+
+ AppCompatOverridesParser(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Parses the given {@code configStr} and returns a map from package name to a set of change
+ * IDs to remove for that package.
+ *
+ * <p>The given {@code configStr} is expected to either be:
+ *
+ * <ul>
+ * <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+ * ownedChangeIds}, for all installed packages should be removed.
+ * <li>A comma separated key value list, where the key is a package name and the value is
+ * either:
+ * <ul>
+ * <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+ * ownedChangeIds} for that package should be removed.
+ * <li>A colon separated list of change IDs to remove for that package.
+ * </ul>
+ * </ul>
+ *
+ * <p>If the given {@code configStr} doesn't match the expected format, an empty map will be
+ * returned. If a specific change ID isn't a valid long, it will be ignored.
+ */
+ Map<String, Set<Long>> parseRemoveOverrides(String configStr, Set<Long> ownedChangeIds) {
+ if (configStr.isEmpty()) {
+ return emptyMap();
+ }
+
+ Map<String, Set<Long>> result = new ArrayMap<>();
+ if (configStr.equals(WILDCARD_SYMBOL)) {
+ if (ownedChangeIds.isEmpty()) {
+ Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+ return emptyMap();
+ }
+ List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(
+ MATCH_ANY_USER);
+ for (ApplicationInfo appInfo : installedApps) {
+ result.put(appInfo.packageName, ownedChangeIds);
+ }
+ return result;
+ }
+
+ KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(configStr);
+ } catch (IllegalArgumentException e) {
+ Slog.w(
+ TAG,
+ "Invalid format in '" + FLAG_REMOVE_OVERRIDES + "' flag: " + configStr, e);
+ return emptyMap();
+ }
+ for (int i = 0; i < parser.size(); i++) {
+ String packageName = parser.keyAt(i);
+ String changeIdsStr = parser.getString(packageName, /* def= */ "");
+ if (changeIdsStr.equals(WILDCARD_SYMBOL)) {
+ if (ownedChangeIds.isEmpty()) {
+ Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+ continue;
+ }
+ result.put(packageName, ownedChangeIds);
+ } else {
+ for (String changeIdStr : changeIdsStr.split(":")) {
+ try {
+ long changeId = Long.parseLong(changeIdStr);
+ result.computeIfAbsent(packageName, k -> new ArraySet<>()).add(changeId);
+ } catch (NumberFormatException e) {
+ Slog.w(
+ TAG,
+ "Invalid change ID in '" + FLAG_REMOVE_OVERRIDES + "' flag: "
+ + changeIdStr, e);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Parses the given {@code configStr}, that is expected to be a comma separated list of change
+ * IDs, into a set.
+ *
+ * <p>If any of the change IDs isn't a valid long, it will be ignored.
+ */
+ static Set<Long> parseOwnedChangeIds(String configStr) {
+ if (configStr.isEmpty()) {
+ return emptySet();
+ }
+
+ Set<Long> result = new ArraySet<>();
+ for (String changeIdStr : configStr.split(",")) {
+ try {
+ result.add(Long.parseLong(changeIdStr));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG,
+ "Invalid change ID in '" + FLAG_OWNED_CHANGE_IDS + "' flag: " + changeIdStr,
+ e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parses the given {@code configStr}, that is expected to be a comma separated list of changes
+ * overrides, and returns a {@link PackageOverrides}.
+ *
+ * <p>Each change override is in the following format:
+ * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled?>'. If <enabled> is empty,
+ * this indicates that any override for the specified change ID should be removed.
+ *
+ * <p>If there are multiple overrides that should be added with the same change ID, the one
+ * that best fits the given {@code versionCode} is added.
+ *
+ * <p>Any overrides whose change ID is in {@code changeIdsToSkip} are ignored.
+ *
+ * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. If the
+ * same change ID is both added and removed, i.e., has a change override entry with an empty
+ * enabled and another with a non-empty enabled, the change ID will only be removed.
+ */
+ static PackageOverrides parsePackageOverrides(
+ String configStr, long versionCode, Set<Long> changeIdsToSkip) {
+ if (configStr.isEmpty()) {
+ return new PackageOverrides();
+ }
+ PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode);
+ Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>();
+ Set<Long> overridesToRemove = new ArraySet<>();
+ for (String overrideEntryString : configStr.split(",")) {
+ List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4));
+ if (changeIdAndVersions.size() != 4) {
+ Slog.w(TAG, "Invalid change override entry: " + overrideEntryString);
+ continue;
+ }
+ long changeId;
+ try {
+ changeId = Long.parseLong(changeIdAndVersions.get(0));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid change ID in override entry: " + overrideEntryString, e);
+ continue;
+ }
+
+ if (changeIdsToSkip.contains(changeId)) {
+ continue;
+ }
+
+ String minVersionCodeStr = changeIdAndVersions.get(1);
+ String maxVersionCodeStr = changeIdAndVersions.get(2);
+
+ String enabledStr = changeIdAndVersions.get(3);
+ if (enabledStr.isEmpty()) {
+ if (!minVersionCodeStr.isEmpty() || !maxVersionCodeStr.isEmpty()) {
+ Slog.w(
+ TAG,
+ "min/max version code should be empty if enabled is empty: "
+ + overrideEntryString);
+ }
+ overridesToRemove.add(changeId);
+ continue;
+ }
+ if (!BOOLEAN_PATTERN.matcher(enabledStr).matches()) {
+ Slog.w(TAG, "Invalid enabled string in override entry: " + overrideEntryString);
+ continue;
+ }
+ boolean enabled = Boolean.parseBoolean(enabledStr);
+ PackageOverride.Builder overrideBuilder = new PackageOverride.Builder().setEnabled(
+ enabled);
+ try {
+ if (!minVersionCodeStr.isEmpty()) {
+ overrideBuilder.setMinVersionCode(Long.parseLong(minVersionCodeStr));
+ }
+ if (!maxVersionCodeStr.isEmpty()) {
+ overrideBuilder.setMaxVersionCode(Long.parseLong(maxVersionCodeStr));
+ }
+ } catch (NumberFormatException e) {
+ Slog.w(TAG,
+ "Invalid min/max version code in override entry: " + overrideEntryString,
+ e);
+ continue;
+ }
+
+ try {
+ PackageOverride override = overrideBuilder.build();
+ if (!overridesToAdd.containsKey(changeId)
+ || comparator.compare(override, overridesToAdd.get(changeId)) < 0) {
+ overridesToAdd.put(changeId, override);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed to build PackageOverride", e);
+ }
+ }
+
+ for (Long changeId : overridesToRemove) {
+ if (overridesToAdd.containsKey(changeId)) {
+ Slog.w(
+ TAG,
+ "Change ID ["
+ + changeId
+ + "] is both added and removed in package override flag: "
+ + configStr);
+ overridesToAdd.remove(changeId);
+ }
+ }
+
+ return new PackageOverrides(overridesToAdd, overridesToRemove);
+ }
+
+ /**
+ * A container for a map from change ID to {@link PackageOverride} to add and a set of change
+ * IDs to remove overrides for.
+ *
+ * <p>The map of overrides to add and the set of overrides to remove are mutually exclusive.
+ */
+ static final class PackageOverrides {
+ public final Map<Long, PackageOverride> overridesToAdd;
+ public final Set<Long> overridesToRemove;
+
+ PackageOverrides() {
+ this(emptyMap(), emptySet());
+ }
+
+ PackageOverrides(Map<Long, PackageOverride> overridesToAdd, Set<Long> overridesToRemove) {
+ this.overridesToAdd = overridesToAdd;
+ this.overridesToRemove = overridesToRemove;
+ }
+ }
+
+ /**
+ * A {@link Comparator} that compares @link PackageOverride} instances with respect to a
+ * specified {@code versionCode} as follows:
+ *
+ * <ul>
+ * <li>Prefer the {@link PackageOverride} whose version range contains {@code versionCode}.
+ * <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+ * versionCode} from below.
+ * <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+ * versionCode} from above.
+ * </ul>
+ */
+ private static final class PackageOverrideComparator implements Comparator<PackageOverride> {
+ private final long mVersionCode;
+
+ PackageOverrideComparator(long versionCode) {
+ this.mVersionCode = versionCode;
+ }
+
+ @Override
+ public int compare(PackageOverride o1, PackageOverride o2) {
+ // Prefer overrides whose version range contains versionCode.
+ boolean isVersionInRange1 = isVersionInRange(o1, mVersionCode);
+ boolean isVersionInRange2 = isVersionInRange(o2, mVersionCode);
+ if (isVersionInRange1 != isVersionInRange2) {
+ return isVersionInRange1 ? -1 : 1;
+ }
+
+ // Otherwise, prefer overrides whose version range is before versionCode.
+ boolean isVersionAfterRange1 = isVersionAfterRange(o1, mVersionCode);
+ boolean isVersionAfterRange2 = isVersionAfterRange(o2, mVersionCode);
+ if (isVersionAfterRange1 != isVersionAfterRange2) {
+ return isVersionAfterRange1 ? -1 : 1;
+ }
+
+ // If both overrides' version ranges are either before or after versionCode, prefer
+ // those whose version range is closer to versionCode.
+ return Long.compare(
+ getVersionProximity(o1, mVersionCode), getVersionProximity(o2, mVersionCode));
+ }
+
+ /**
+ * Returns true if the version range in the given {@code override} contains {@code
+ * versionCode}.
+ */
+ private static boolean isVersionInRange(PackageOverride override, long versionCode) {
+ return override.getMinVersionCode() <= versionCode
+ && versionCode <= override.getMaxVersionCode();
+ }
+
+ /**
+ * Returns true if the given {@code versionCode} is strictly after the version range in the
+ * given {@code override}.
+ */
+ private static boolean isVersionAfterRange(PackageOverride override, long versionCode) {
+ return override.getMaxVersionCode() < versionCode;
+ }
+
+ /**
+ * Returns true if the given {@code versionCode} is strictly before the version range in the
+ * given {@code override}.
+ */
+ private static boolean isVersionBeforeRange(PackageOverride override, long versionCode) {
+ return override.getMinVersionCode() > versionCode;
+ }
+
+ /**
+ * In case the given {@code versionCode} is strictly before or after the version range in
+ * the given {@code override}, returns the distance from it, otherwise returns zero.
+ */
+ private static long getVersionProximity(PackageOverride override, long versionCode) {
+ if (isVersionAfterRange(override, versionCode)) {
+ return versionCode - override.getMaxVersionCode();
+ }
+ if (isVersionBeforeRange(override, versionCode)) {
+ return override.getMinVersionCode() - versionCode;
+ }
+
+ // Version is in range. Note that when two overrides have a zero version proximity
+ // they will be ordered arbitrarily.
+ return 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
new file mode 100644
index 000000000000..63ae1af42f08
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -0,0 +1,408 @@
+/*
+ * 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.compat.overrides;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.provider.DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES;
+
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.SystemService;
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service for applying per-app compat overrides delivered via Device Config.
+ *
+ * <p>The service listens both on changes to supported Device Config namespaces and on package
+ * added/changed/removed events, and applies overrides accordingly.
+ *
+ * @hide
+ */
+public final class AppCompatOverridesService {
+ private static final String TAG = "AppCompatOverridesService";
+
+ private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(
+ NAMESPACE_APP_COMPAT_OVERRIDES);
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final IPlatformCompat mPlatformCompat;
+ private final List<String> mSupportedNamespaces;
+ private final AppCompatOverridesParser mOverridesParser;
+ private final PackageReceiver mPackageReceiver;
+ private final List<DeviceConfigListener> mDeviceConfigListeners;
+
+ private AppCompatOverridesService(Context context) {
+ this(context, IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)), SUPPORTED_NAMESPACES);
+ }
+
+ @VisibleForTesting
+ AppCompatOverridesService(Context context, IPlatformCompat platformCompat,
+ List<String> supportedNamespaces) {
+ mContext = context;
+ mPackageManager = mContext.getPackageManager();
+ mPlatformCompat = platformCompat;
+ mSupportedNamespaces = supportedNamespaces;
+ mOverridesParser = new AppCompatOverridesParser(mPackageManager);
+ mPackageReceiver = new PackageReceiver(mContext);
+ mDeviceConfigListeners = new ArrayList<>();
+ for (String namespace : mSupportedNamespaces) {
+ mDeviceConfigListeners.add(new DeviceConfigListener(mContext, namespace));
+ }
+ }
+
+ @Override
+ public void finalize() {
+ unregisterDeviceConfigListeners();
+ unregisterPackageReceiver();
+ }
+
+ @VisibleForTesting
+ void registerDeviceConfigListeners() {
+ for (DeviceConfigListener listener : mDeviceConfigListeners) {
+ listener.register();
+ }
+ }
+
+ private void unregisterDeviceConfigListeners() {
+ for (DeviceConfigListener listener : mDeviceConfigListeners) {
+ listener.unregister();
+ }
+ }
+
+ @VisibleForTesting
+ void registerPackageReceiver() {
+ mPackageReceiver.register();
+ }
+
+ private void unregisterPackageReceiver() {
+ mPackageReceiver.unregister();
+ }
+
+ /**
+ * Same as {@link #applyOverrides(Properties, Map)} except all properties of the given {@code
+ * namespace} are fetched via {@link DeviceConfig#getProperties}.
+ */
+ private void applyAllOverrides(String namespace,
+ Map<String, Set<Long>> packageToChangeIdsToSkip) {
+ applyOverrides(DeviceConfig.getProperties(namespace), packageToChangeIdsToSkip);
+ }
+
+ /**
+ * Iterates all package override flags in the given {@code properties}, and for each flag whose
+ * package is installed on the device, parses its value and applies the overrides in it with
+ * respect to the package's current installed version.
+ */
+ private void applyOverrides(Properties properties,
+ Map<String, Set<Long>> packageToChangeIdsToSkip) {
+ Set<String> packageNames = new ArraySet<>(properties.getKeyset());
+ packageNames.remove(FLAG_OWNED_CHANGE_IDS);
+ packageNames.remove(FLAG_REMOVE_OVERRIDES);
+ for (String packageName : packageNames) {
+ Long versionCode = getVersionCodeOrNull(packageName);
+ if (versionCode == null) {
+ // Package isn't installed yet.
+ continue;
+ }
+
+ applyPackageOverrides(properties.getString(packageName, /* defaultValue= */ ""),
+ packageName, versionCode,
+ packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()));
+ }
+ }
+
+ /**
+ * Applies all overrides in all supported namespaces for the given {@code packageName}.
+ */
+ private void applyAllPackageOverrides(String packageName) {
+ Long versionCode = getVersionCodeOrNull(packageName);
+ if (versionCode == null) {
+ return;
+ }
+
+ for (String namespace : mSupportedNamespaces) {
+ // We apply overrides for each namespace separately so that if there is a failure for
+ // one namespace, the other namespaces won't be affected.
+ applyPackageOverrides(
+ DeviceConfig.getString(namespace, packageName, /* defaultValue= */ ""),
+ packageName, versionCode,
+ getOverridesToRemove(namespace).getOrDefault(packageName, emptySet()));
+ }
+ }
+
+ /**
+ * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments, adds the
+ * resulting {@link PackageOverrides#overridesToAdd} via {@link
+ * IPlatformCompat#putOverridesOnReleaseBuilds}, and removes the resulting {@link
+ * PackageOverrides#overridesToRemove} via {@link
+ * IPlatformCompat#removeOverridesOnReleaseBuilds}.
+ */
+ private void applyPackageOverrides(String configStr, String packageName,
+ long versionCode, Set<Long> changeIdsToSkip) {
+ PackageOverrides packageOverrides = AppCompatOverridesParser.parsePackageOverrides(
+ configStr, versionCode, changeIdsToSkip);
+ putPackageOverrides(packageName, packageOverrides.overridesToAdd);
+ removePackageOverrides(packageName, packageOverrides.overridesToRemove);
+ }
+
+ /**
+ * Removes all owned overrides in all supported namespaces for the given {@code packageName}.
+ *
+ * <p>If a certain namespace doesn't have a package override flag for the given {@code
+ * packageName}, that namespace is skipped.</p>
+ */
+ private void removeAllPackageOverrides(String packageName) {
+ for (String namespace : mSupportedNamespaces) {
+ if (DeviceConfig.getString(namespace, packageName, /* defaultValue= */ "").isEmpty()) {
+ // No overrides for this package in this namespace.
+ continue;
+ }
+ // We remove overrides for each namespace separately so that if there is a failure for
+ // one namespace, the other namespaces won't be affected.
+ removePackageOverrides(packageName, getOwnedChangeIds(namespace));
+ }
+ }
+
+ /**
+ * Calls {@link IPlatformCompat#removeOverridesOnReleaseBuilds} on each package name and
+ * respective change IDs in {@code overridesToRemove}.
+ */
+ private void removeOverrides(Map<String, Set<Long>> overridesToRemove) {
+ for (Map.Entry<String, Set<Long>> packageNameAndOverrides : overridesToRemove.entrySet()) {
+ removePackageOverrides(packageNameAndOverrides.getKey(),
+ packageNameAndOverrides.getValue());
+ }
+ }
+
+ /**
+ * Fetches the value of {@link AppCompatOverridesParser#FLAG_REMOVE_OVERRIDES} for the given
+ * {@code namespace} and parses it into a map from package name to a set of change IDs to
+ * remove for that package.
+ */
+ private Map<String, Set<Long>> getOverridesToRemove(String namespace) {
+ return mOverridesParser.parseRemoveOverrides(
+ DeviceConfig.getString(namespace, FLAG_REMOVE_OVERRIDES, /* defaultValue= */ ""),
+ getOwnedChangeIds(namespace));
+ }
+
+ /**
+ * Fetches the value of {@link AppCompatOverridesParser#FLAG_OWNED_CHANGE_IDS} for the given
+ * {@code namespace} and parses it into a set of change IDs.
+ */
+ private static Set<Long> getOwnedChangeIds(String namespace) {
+ return AppCompatOverridesParser.parseOwnedChangeIds(
+ DeviceConfig.getString(namespace, FLAG_OWNED_CHANGE_IDS, /* defaultValue= */ ""));
+ }
+
+ private void putPackageOverrides(String packageName,
+ Map<Long, PackageOverride> overridesToAdd) {
+ if (overridesToAdd.isEmpty()) {
+ return;
+ }
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overridesToAdd);
+ try {
+ mPlatformCompat.putOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
+ }
+ }
+
+ private void removePackageOverrides(String packageName, Set<Long> overridesToRemove) {
+ if (overridesToRemove.isEmpty()) {
+ return;
+ }
+ CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+ overridesToRemove);
+ try {
+ mPlatformCompat.removeOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IPlatformCompat#removeOverridesOnReleaseBuilds", e);
+ }
+ }
+
+ private boolean isInstalledForAnyUser(String packageName) {
+ return getVersionCodeOrNull(packageName) != null;
+ }
+
+ @Nullable
+ private Long getVersionCodeOrNull(String packageName) {
+ try {
+ ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName,
+ MATCH_ANY_USER);
+ return applicationInfo.longVersionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package isn't installed for any user.
+ return null;
+ }
+ }
+
+ /**
+ * SystemService lifecycle for AppCompatOverridesService.
+ *
+ * @hide
+ */
+ public static final class Lifecycle extends SystemService {
+ private AppCompatOverridesService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new AppCompatOverridesService(getContext());
+ mService.registerDeviceConfigListeners();
+ mService.registerPackageReceiver();
+ }
+ }
+
+ /**
+ * A {@link DeviceConfig.OnPropertiesChangedListener} that listens on changes to a given
+ * namespace and adds/removes overrides according to the changed flags.
+ */
+ private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+ private final Context mContext;
+ private final String mNamespace;
+
+ private DeviceConfigListener(Context context, String namespace) {
+ mContext = context;
+ mNamespace = namespace;
+ }
+
+ private void register() {
+ DeviceConfig.addOnPropertiesChangedListener(mNamespace, mContext.getMainExecutor(),
+ this);
+ }
+
+ private void unregister() {
+ DeviceConfig.removeOnPropertiesChangedListener(this);
+ }
+
+ @Override
+ public void onPropertiesChanged(Properties properties) {
+ boolean removeOverridesFlagChanged = properties.getKeyset().contains(
+ FLAG_REMOVE_OVERRIDES);
+ boolean ownedChangedIdsFlagChanged = properties.getKeyset().contains(
+ FLAG_OWNED_CHANGE_IDS);
+
+ Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace);
+ if (removeOverridesFlagChanged || ownedChangedIdsFlagChanged) {
+ // In both cases it's possible that overrides that weren't removed before should
+ // now be removed.
+ removeOverrides(overridesToRemove);
+ }
+
+ if (removeOverridesFlagChanged) {
+ // We need to re-apply all overrides in the namespace since the remove overrides
+ // flag might have blocked some of them from being applied before.
+ applyAllOverrides(mNamespace, overridesToRemove);
+ } else {
+ applyOverrides(properties, overridesToRemove);
+ }
+ }
+ }
+
+ /**
+ * A {@link BroadcastReceiver} that listens on package added/changed/removed events and
+ * adds/removes overrides according to the corresponding Device Config flags.
+ */
+ private final class PackageReceiver extends BroadcastReceiver {
+ private final Context mContext;
+ private final IntentFilter mIntentFilter;
+
+ private PackageReceiver(Context context) {
+ mContext = context;
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(ACTION_PACKAGE_ADDED);
+ mIntentFilter.addAction(ACTION_PACKAGE_CHANGED);
+ mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ mIntentFilter.addDataScheme("package");
+ }
+
+ private void register() {
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, /* broadcastPermission= */
+ null, /* scheduler= */ null);
+ }
+
+ private void unregister() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+ Uri data = intent.getData();
+ if (data == null) {
+ Slog.w(TAG, "Failed to get package name in package receiver");
+ return;
+ }
+ String packageName = data.getSchemeSpecificPart();
+ String action = intent.getAction();
+ if (action == null) {
+ Slog.w(TAG, "Failed to get action in package receiver");
+ return;
+ }
+ switch (action) {
+ case ACTION_PACKAGE_ADDED:
+ case ACTION_PACKAGE_CHANGED:
+ applyAllPackageOverrides(packageName);
+ break;
+ case ACTION_PACKAGE_REMOVED:
+ if (!isInstalledForAnyUser(packageName)) {
+ removeAllPackageOverrides(packageName);
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unsupported action in package receiver: " + action);
+ break;
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/compat/overrides/OWNERS b/services/core/java/com/android/server/compat/overrides/OWNERS
new file mode 100644
index 000000000000..b80f3402c19d
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/OWNERS
@@ -0,0 +1,2 @@
+tomnatan@google.com
+mariiasand@google.com
diff --git a/services/core/java/com/android/server/compat/overrides/TEST_MAPPING b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
new file mode 100644
index 000000000000..4b8f08ec9164
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.compat.overrides"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1acbde91b353..565c9ae2142c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -24,8 +24,7 @@ import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
import android.Manifest;
import android.annotation.NonNull;
@@ -1098,13 +1097,14 @@ public class Vpn {
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
- return Binder.withCleanCallingIdentity(() -> {
- try {
- return pm.getPackageUidAsUser(app, userId);
- } catch (NameNotFoundException e) {
- return -1;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return pm.getPackageUidAsUser(app, userId);
+ } catch (NameNotFoundException e) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private boolean doesPackageTargetAtLeastQ(String packageName) {
@@ -1280,15 +1280,16 @@ public class Vpn {
// We are user controlled, not driven by NetworkRequest.
}
};
- Binder.withCleanCallingIdentity(() -> {
- try {
- mNetworkAgent.register();
- } catch (final Exception e) {
- // If register() throws, don't keep an unregistered agent.
- mNetworkAgent = null;
- throw e;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkAgent.register();
+ } catch (final Exception e) {
+ // If register() throws, don't keep an unregistered agent.
+ mNetworkAgent = null;
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
? Arrays.asList(mConfig.underlyingNetworks) : null);
updateState(DetailedState.CONNECTED, "agentConnect");
@@ -2026,13 +2027,16 @@ public class Vpn {
}
private void enforceNotRestrictedUser() {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
}
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -2825,8 +2829,10 @@ public class Vpn {
LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
super(TAG);
- checkArgument(racoon != null || mtpd != null, "Arguments to racoon and mtpd "
- + "must not both be null");
+ if (racoon == null && mtpd == null) {
+ throw new IllegalArgumentException(
+ "Arguments to racoon and mtpd must not both be null");
+ }
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
// TODO: clear arguments from memory once launched
@@ -3151,8 +3157,8 @@ public class Vpn {
*/
public synchronized boolean provisionVpnProfile(
@NonNull String packageName, @NonNull VpnProfile profile) {
- checkNotNull(packageName, "No package name provided");
- checkNotNull(profile, "No profile provided");
+ requireNonNull(packageName, "No package name provided");
+ requireNonNull(profile, "No profile provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
@@ -3169,12 +3175,12 @@ public class Vpn {
}
// Permissions checked during startVpnProfile()
- Binder.withCleanCallingIdentity(
- () -> {
- getVpnProfileStore().put(
- getProfileNameForPackage(packageName),
- encodedProfile);
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ getVpnProfileStore().put(getProfileNameForPackage(packageName), encodedProfile);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
// This mirrors the prepareAndAuthorize that is used by VpnService.
@@ -3194,26 +3200,28 @@ public class Vpn {
*/
public synchronized void deleteVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
- Binder.withCleanCallingIdentity(
- () -> {
- // If this profile is providing the current VPN, turn it off, disabling
- // always-on as well if enabled.
- if (isCurrentIkev2VpnLocked(packageName)) {
- if (mAlwaysOn) {
- // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
- setAlwaysOnPackage(null, false, null);
- } else {
- prepareInternal(VpnConfig.LEGACY_VPN);
- }
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // If this profile is providing the current VPN, turn it off, disabling
+ // always-on as well if enabled.
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ if (mAlwaysOn) {
+ // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+ setAlwaysOnPackage(null, false, null);
+ } else {
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
+ }
- getVpnProfileStore().remove(getProfileNameForPackage(packageName));
- });
+ getVpnProfileStore().remove(getProfileNameForPackage(packageName));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -3247,7 +3255,7 @@ public class Vpn {
*/
public synchronized void startVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
@@ -3256,15 +3264,17 @@ public class Vpn {
throw new SecurityException("User consent not granted for package " + packageName);
}
- Binder.withCleanCallingIdentity(
- () -> {
- final VpnProfile profile = getVpnProfilePrivileged(packageName);
- if (profile == null) {
- throw new IllegalArgumentException("No profile found for " + packageName);
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final VpnProfile profile = getVpnProfilePrivileged(packageName);
+ if (profile == null) {
+ throw new IllegalArgumentException("No profile found for " + packageName);
+ }
- startVpnProfilePrivileged(profile, packageName);
- });
+ startVpnProfilePrivileged(profile, packageName);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private synchronized void startVpnProfilePrivileged(
@@ -3325,7 +3335,7 @@ public class Vpn {
* @param packageName the package name of the app provisioning this profile
*/
public synchronized void stopVpnProfile(@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 35f29579b417..9f806af5b444 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -43,6 +43,7 @@ abstract class DisplayDevice {
// The display device does not manage these properties itself, they are set by
// the display manager service. The display device shouldn't really be looking at these.
private int mCurrentLayerStack = -1;
+ private int mCurrentFlags = 0;
private int mCurrentOrientation = -1;
private Rect mCurrentLayerStackRect;
private Rect mCurrentDisplayRect;
@@ -212,6 +213,19 @@ abstract class DisplayDevice {
}
/**
+ * Sets the display flags while in a transaction.
+ *
+ * Valid display flags:
+ * {@link SurfaceControl#DISPLAY_RECEIVES_INPUT}
+ */
+ public final void setDisplayFlagsLocked(SurfaceControl.Transaction t, int flags) {
+ if (mCurrentFlags != flags) {
+ mCurrentFlags = flags;
+ t.setDisplayFlags(mDisplayToken, flags);
+ }
+ }
+
+ /**
* Sets the display projection while in a transaction.
*
* @param orientation defines the display's orientation
@@ -298,6 +312,7 @@ abstract class DisplayDevice {
pw.println("mUniqueId=" + mUniqueId);
pw.println("mDisplayToken=" + mDisplayToken);
pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+ pw.println("mCurrentFlags=" + mCurrentFlags);
pw.println("mCurrentOrientation=" + mCurrentOrientation);
pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index f23ae6e2340c..b0ad11816539 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -2057,7 +2057,9 @@ public class DisplayModeDirector {
public void observe() {
StatusBarManagerInternal statusBar =
LocalServices.getService(StatusBarManagerInternal.class);
- statusBar.setUdfpsHbmListener(this);
+ if (statusBar != null) {
+ statusBar.setUdfpsHbmListener(this);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 9acb4c8f471a..5186744d5c27 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -512,6 +514,11 @@ final class LogicalDisplay {
boolean isBlanked) {
// Set the layer stack.
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
+ // Also inform whether the device is the same one sent to inputflinger for its layerstack.
+ // TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
+ device.setDisplayFlagsLocked(t,
+ device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE
+ ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0);
// Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index bccd4c42ff12..f9a1368ff3e8 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -162,7 +162,8 @@ public final class ColorDisplayService extends SystemService {
private final ReduceBrightColorsTintController mReduceBrightColorsTintController =
new ReduceBrightColorsTintController();
- private final Handler mHandler;
+ @VisibleForTesting
+ final Handler mHandler;
private final AppSaturationController mAppSaturationController = new AppSaturationController();
@@ -404,13 +405,13 @@ public final class ColorDisplayService extends SystemService {
// existing activated state. This ensures consistency of tint across the color mode change.
onDisplayColorModeChanged(getColorModeInternal());
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
if (mNightDisplayTintController.isAvailable(getContext())) {
// Reset the activated state.
mNightDisplayTintController.setActivated(null);
// Prepare the night display color transformation matrix.
- mNightDisplayTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
@@ -432,8 +433,7 @@ public final class ColorDisplayService extends SystemService {
}
if (mReduceBrightColorsTintController.isAvailable(getContext())) {
- mReduceBrightColorsTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
onReduceBrightColorsStrengthLevelChanged();
final boolean reset = resetReduceBrightColors();
if (!reset) {
@@ -540,8 +540,8 @@ public final class ColorDisplayService extends SystemService {
mDisplayWhiteBalanceTintController.cancelAnimator();
if (mNightDisplayTintController.isAvailable(getContext())) {
- mNightDisplayTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
}
@@ -736,10 +736,11 @@ public final class ColorDisplayService extends SystemService {
@VisibleForTesting
void updateDisplayWhiteBalanceStatus() {
boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
&& !mNightDisplayTintController.isActivated()
&& !isAccessibilityEnabled()
- && DisplayTransformManager.needsLinearColorMatrix());
+ && dtm.needsLinearColorMatrix());
boolean activated = mDisplayWhiteBalanceTintController.isActivated();
if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index 5c68c51ad7a3..0dba9e1b0af1 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -239,7 +239,7 @@ public class DisplayTransformManager {
/**
* Return true when the color matrix works in linear space.
*/
- public static boolean needsLinearColorMatrix() {
+ public boolean needsLinearColorMatrix() {
return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED;
}
@@ -247,7 +247,7 @@ public class DisplayTransformManager {
/**
* Return true when the specified colorMode requires the color matrix to work in linear space.
*/
- public static boolean needsLinearColorMatrix(int colorMode) {
+ public boolean needsLinearColorMatrix(int colorMode) {
return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED;
}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 8405bbe38b12..d422d51d4087 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -88,7 +88,7 @@ final class ActiveSourceHandler {
tv.updateActiveSource(current, "ActiveSourceHandler");
invokeCallback(HdmiControlManager.RESULT_SUCCESS);
} else {
- tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true,
+ tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress,
mCallback);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index fefe9530fa83..5de89c9a9cff 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -317,12 +317,25 @@ public class HdmiCecConfig {
R.bool.config_cecHdmiCecVersion20_allowed,
R.bool.config_cecHdmiCecVersion20_default);
+ Setting routingControlControl = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+ R.bool.config_cecRoutingControl_userConfigurable);
+ routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_ENABLED,
+ R.bool.config_cecRoutingControlEnabled_allowed,
+ R.bool.config_cecRoutingControlEnabled_default);
+ routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_DISABLED,
+ R.bool.config_cecRoutingControlDisabled_allowed,
+ R.bool.config_cecRoutingControlDisabled_default);
+
Setting powerControlMode = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
R.bool.config_cecPowerControlMode_userConfigurable);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
R.bool.config_cecPowerControlModeTv_allowed,
R.bool.config_cecPowerControlModeTv_default);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
R.bool.config_cecPowerControlModeBroadcast_allowed,
R.bool.config_cecPowerControlModeBroadcast_default);
@@ -342,6 +355,16 @@ public class HdmiCecConfig {
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+ Setting systemAudioControl = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+ R.bool.config_cecSystemAudioControl_userConfigurable);
+ systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED,
+ R.bool.config_cecSystemAudioControlEnabled_allowed,
+ R.bool.config_cecSystemAudioControlEnabled_default);
+ systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_DISABLED,
+ R.bool.config_cecSystemAudioControlDisabled_allowed,
+ R.bool.config_cecSystemAudioControlDisabled_default);
+
Setting systemAudioModeMuting = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
R.bool.config_cecSystemAudioModeMuting_userConfigurable);
@@ -498,12 +521,16 @@ public class HdmiCecConfig {
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
@@ -535,12 +562,16 @@ public class HdmiCecConfig {
return Global.HDMI_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+ return Global.HDMI_CEC_SWITCH_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
return Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+ return Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 23b5c1411b0e..698ee0bb6220 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -481,4 +481,113 @@ final class HdmiCecKeycode {
// return muting ? CEC_KEYCODE_MUTE_FUNCTION : CEC_KEYCODE_RESTORE_VOLUME_FUNCTION;
return CEC_KEYCODE_MUTE;
}
+
+ public static String getKeycodeType(byte keycode) {
+ switch (keycode) {
+ case CEC_KEYCODE_UP:
+ case CEC_KEYCODE_DOWN:
+ case CEC_KEYCODE_LEFT:
+ case CEC_KEYCODE_RIGHT:
+ case CEC_KEYCODE_RIGHT_UP:
+ case CEC_KEYCODE_RIGHT_DOWN:
+ case CEC_KEYCODE_LEFT_UP:
+ case CEC_KEYCODE_LEFT_DOWN:
+ case CEC_KEYCODE_PAGE_UP:
+ case CEC_KEYCODE_PAGE_DOWN:
+ case CEC_KEYCODE_EXIT:
+ return "Navigation";
+ case CEC_KEYCODE_ROOT_MENU:
+ case CEC_KEYCODE_SETUP_MENU:
+ case CEC_KEYCODE_CONTENTS_MENU:
+ case CEC_KEYCODE_FAVORITE_MENU:
+ case CEC_KEYCODE_MEDIA_TOP_MENU:
+ case CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU:
+ return "Menu";
+ case CEC_KEYCODE_VOLUME_UP:
+ return "Volume up";
+ case CEC_KEYCODE_VOLUME_DOWN:
+ return "Volume down";
+ case CEC_KEYCODE_MUTE:
+ return "Volume mute";
+ case CEC_KEYCODE_POWER:
+ return "Power";
+ case CEC_KEYCODE_POWER_TOGGLE_FUNCTION:
+ return "Power toggle";
+ case CEC_KEYCODE_POWER_OFF_FUNCTION:
+ return "Power off";
+ case CEC_KEYCODE_POWER_ON_FUNCTION:
+ return "Power on";
+ case CEC_KEYCODE_F1_BLUE:
+ case CEC_KEYCODE_F2_RED:
+ case CEC_KEYCODE_F3_GREEN:
+ case CEC_KEYCODE_F4_YELLOW:
+ case CEC_KEYCODE_F5:
+ return "Function key";
+ case CEC_KEYCODE_NEXT_FAVORITE:
+ case CEC_KEYCODE_CHANNEL_UP:
+ case CEC_KEYCODE_CHANNEL_DOWN:
+ case CEC_KEYCODE_PREVIOUS_CHANNEL:
+ return "Channel";
+ case CEC_KEYCODE_NUMBER_11:
+ case CEC_KEYCODE_NUMBER_12:
+ case CEC_KEYCODE_NUMBER_0_OR_NUMBER_10:
+ case CEC_KEYCODE_NUMBERS_1:
+ case CEC_KEYCODE_NUMBERS_2:
+ case CEC_KEYCODE_NUMBERS_3:
+ case CEC_KEYCODE_NUMBERS_4:
+ case CEC_KEYCODE_NUMBERS_5:
+ case CEC_KEYCODE_NUMBERS_6:
+ case CEC_KEYCODE_NUMBERS_7:
+ case CEC_KEYCODE_NUMBERS_8:
+ case CEC_KEYCODE_NUMBERS_9:
+ return "Number";
+ case CEC_KEYCODE_PLAY:
+ case CEC_KEYCODE_STOP:
+ case CEC_KEYCODE_PAUSE:
+ case CEC_KEYCODE_RECORD:
+ case CEC_KEYCODE_REWIND:
+ case CEC_KEYCODE_FAST_FORWARD:
+ case CEC_KEYCODE_EJECT:
+ case CEC_KEYCODE_FORWARD:
+ case CEC_KEYCODE_BACKWARD:
+ case CEC_KEYCODE_STOP_RECORD:
+ case CEC_KEYCODE_PAUSE_RECORD:
+ case CEC_KEYCODE_ANGLE:
+ case CEC_KEYCODE_SUB_PICTURE:
+ case CEC_KEYCODE_VIDEO_ON_DEMAND:
+ return "Media";
+ case CEC_KEYCODE_PLAY_FUNCTION:
+ case CEC_KEYCODE_PAUSE_PLAY_FUNCTION:
+ case CEC_KEYCODE_RECORD_FUNCTION:
+ case CEC_KEYCODE_PAUSE_RECORD_FUNCTION:
+ case CEC_KEYCODE_STOP_FUNCTION:
+ case CEC_KEYCODE_MUTE_FUNCTION:
+ case CEC_KEYCODE_RESTORE_VOLUME_FUNCTION:
+ case CEC_KEYCODE_TUNE_FUNCTION:
+ case CEC_KEYCODE_SELECT_MEDIA_FUNCTION:
+ case CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION:
+ case CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION:
+ return "Functional";
+ case CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE:
+ case CEC_KEYCODE_TIMER_PROGRAMMING:
+ return "Timer";
+ case CEC_KEYCODE_SOUND_SELECT:
+ case CEC_KEYCODE_SELECT_SOUND_PRESENTATION:
+ case CEC_KEYCODE_SELECT_BROADCAST_TYPE:
+ case CEC_KEYCODE_INPUT_SELECT:
+ case CEC_KEYCODE_SELECT:
+ return "Select";
+ case CEC_KEYCODE_NUMBER_ENTRY_MODE:
+ case CEC_KEYCODE_DOT:
+ case CEC_KEYCODE_CLEAR:
+ case CEC_KEYCODE_ENTER:
+ case CEC_KEYCODE_DISPLAY_INFORMATION:
+ case CEC_KEYCODE_HELP:
+ case CEC_KEYCODE_DATA:
+ case CEC_KEYCODE_INITIAL_CONFIGURATION:
+ return "General";
+ default:
+ return "Unknown";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a2cb78d27a57..de65d76688f8 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -17,11 +17,11 @@
package com.android.server.hdmi;
import android.annotation.CallSuper;
-import android.annotation.Nullable;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.input.InputManager;
+import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Handler;
@@ -281,11 +281,11 @@ abstract class HdmiCecLocalDevice {
case Constants.MESSAGE_SET_MENU_LANGUAGE:
return handleSetMenuLanguage(message);
case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
- return handleGivePhysicalAddress(null);
+ return handleGivePhysicalAddress(message);
case Constants.MESSAGE_GIVE_OSD_NAME:
return handleGiveOsdName(message);
case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
- return handleGiveDeviceVendorId(null);
+ return handleGiveDeviceVendorId(message);
case Constants.MESSAGE_CEC_VERSION:
return handleCecVersion();
case Constants.MESSAGE_GET_CEC_VERSION:
@@ -380,25 +380,32 @@ abstract class HdmiCecLocalDevice {
@ServiceThreadOnly
@Constants.HandleMessageResult
- protected int handleGivePhysicalAddress(@Nullable SendMessageCallback callback) {
+ protected int handleGivePhysicalAddress(HdmiCecMessage message) {
assertRunOnServiceThread();
-
int physicalAddress = mService.getPhysicalAddress();
- HdmiCecMessage cecMessage =
- HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, physicalAddress, mDeviceType);
- mService.sendCecCommand(cecMessage, callback);
+ if (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+ } else {
+ HdmiCecMessage cecMessage =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, physicalAddress, mDeviceType);
+ mService.sendCecCommand(cecMessage);
+ }
return Constants.HANDLED;
}
@ServiceThreadOnly
@Constants.HandleMessageResult
- protected int handleGiveDeviceVendorId(@Nullable SendMessageCallback callback) {
+ protected int handleGiveDeviceVendorId(HdmiCecMessage message) {
assertRunOnServiceThread();
int vendorId = mService.getVendorId();
- HdmiCecMessage cecMessage =
- HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, vendorId);
- mService.sendCecCommand(cecMessage, callback);
+ if (vendorId == Result.FAILURE_UNKNOWN) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+ } else {
+ HdmiCecMessage cecMessage =
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, vendorId);
+ mService.sendCecCommand(cecMessage);
+ }
return Constants.HANDLED;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0bb128536b42..93b0560aeb97 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -34,7 +34,6 @@ import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
import android.os.SystemProperties;
-import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -108,10 +107,12 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
- mRoutingControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
- mSystemAudioControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mRoutingControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+ == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+ mSystemAudioControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
}
private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
@@ -857,7 +858,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
}
- void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+ void onSystemAudioControlFeatureSupportChanged(boolean enabled) {
setSystemAudioControlFeatureEnabled(enabled);
if (enabled) {
addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
@@ -873,7 +874,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
@ServiceThreadOnly
- void setRoutingControlFeatureEnables(boolean enabled) {
+ void setRoutingControlFeatureEnabled(boolean enabled) {
assertRunOnServiceThread();
synchronized (mLock) {
mRoutingControlFeatureEnabled = enabled;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 37ee76b7c615..1276aa31b320 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -140,13 +140,10 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// Invalidate the internal active source record when going to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
- boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
- == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
if (!wasActiveSource) {
return;
}
- if (initiatedByCec || !mTvSendStandbyOnSleep) {
+ if (initiatedByCec) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
mService.getPhysicalAddress()));
return;
@@ -155,13 +152,20 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
case HdmiControlService.STANDBY_SCREEN_OFF:
// Get latest setting value
@HdmiControlManager.PowerControlMode
- String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ String powerControlMode = mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- switch (sendStandbyOnSleep) {
+ switch (powerControlMode) {
case HdmiControlManager.POWER_CONTROL_MODE_TV:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
break;
+ case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress,
+ Constants.ADDR_AUDIO_SYSTEM));
+ break;
case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 1c726e0a8fbd..0c7b3f685df4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -107,14 +107,18 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected void sendStandby(int deviceId) {
assertRunOnServiceThread();
- String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ String powerControlMode = mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- if (sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
+ if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
return;
}
mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_AUDIO_SYSTEM));
+ }
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index acfeb6c77db2..1d099da18d12 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -38,11 +38,9 @@ import android.hardware.hdmi.HdmiRecordSources;
import android.hardware.hdmi.HdmiTimerRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
-import android.provider.Settings.Global;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -155,8 +153,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_TV);
mPrevPortId = Constants.INVALID_PORT_ID;
- mSystemAudioControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mSystemAudioControlFeatureEnabled = service.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
}
@@ -374,12 +373,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
int newPath = mService.portIdToPath(portId);
- startRoutingControl(oldPath, newPath, true, callback);
+ startRoutingControl(oldPath, newPath, callback);
}
@ServiceThreadOnly
- void startRoutingControl(int oldPath, int newPath, boolean queryDevicePowerStatus,
- IHdmiControlCallback callback) {
+ void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (oldPath == newPath) {
return;
@@ -389,7 +387,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mService.sendCecCommand(routingChange);
removeAction(RoutingControlAction.class);
addAndStartAction(
- new RoutingControlAction(this, newPath, queryDevicePowerStatus, callback));
+ new RoutingControlAction(this, newPath, callback));
}
@ServiceThreadOnly
@@ -411,6 +409,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
@Override
+ protected int findAudioReceiverAddress() {
+ return Constants.ADDR_AUDIO_SYSTEM;
+ }
+
+ @Override
@ServiceThreadOnly
@Constants.HandleMessageResult
protected int handleActiveSource(HdmiCecMessage message) {
@@ -567,7 +570,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (isTailOfActivePath(path, getActivePath())) {
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- startRoutingControl(getActivePath(), newPath, false, null);
+ startRoutingControl(getActivePath(), newPath, null);
return true;
}
return false;
@@ -612,7 +615,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
getActiveSource().invalidate();
removeAction(RoutingControlAction.class);
int newPath = HdmiUtils.twoBytesToInt(params, 2);
- addAndStartAction(new RoutingControlAction(this, newPath, true, null));
+ addAndStartAction(new RoutingControlAction(this, newPath, null));
}
return Constants.HANDLED;
}
@@ -926,10 +929,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
synchronized (mLock) {
mSystemAudioMute = mute;
mSystemAudioVolume = volume;
- int maxVolume = mService.getAudioManager().getStreamMaxVolume(
- AudioManager.STREAM_MUSIC);
- mService.setAudioStatus(mute,
- VolumeControlAction.scaleToCustomVolume(volume, maxVolume));
displayOsd(HdmiControlManager.OSD_MESSAGE_AVR_VOLUME_CHANGED,
mute ? HdmiControlManager.AVR_VOLUME_MUTED : volume);
}
@@ -1189,7 +1188,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Seq #23
if (isTailOfActivePath(path, getActivePath())) {
int newPath = mService.portIdToPath(getActivePortId());
- startRoutingControl(getActivePath(), newPath, true, null);
+ startRoutingControl(getActivePath(), newPath, null);
}
}
@@ -1207,7 +1206,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (!routingForBootup && !isProhibitMode()) {
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- startRoutingControl(getActivePath(), newPath, routingForBootup, null);
+ startRoutingControl(getActivePath(), newPath, null);
}
} else {
int activePath = mService.getPhysicalAddress();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c85fd50c62fe..15a316739134 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -119,6 +119,10 @@ public final class HdmiCecMessage {
if (mParams.length > 0) {
if (filterMessageParameters(mOpcode)) {
s.append(String.format(" <Redacted len=%d>", mParams.length));
+ } else if (isUserControlPressedMessage(mOpcode)) {
+ s.append(
+ String.format(
+ " <Keycode type = %s>", HdmiCecKeycode.getKeycodeType(mParams[0])));
} else {
for (byte data : mParams) {
s.append(String.format(":%02X", data));
@@ -287,7 +291,6 @@ public final class HdmiCecMessage {
private static boolean filterMessageParameters(int opcode) {
switch (opcode) {
- case Constants.MESSAGE_USER_CONTROL_PRESSED:
case Constants.MESSAGE_USER_CONTROL_RELEASED:
case Constants.MESSAGE_SET_OSD_NAME:
case Constants.MESSAGE_SET_OSD_STRING:
@@ -300,5 +303,9 @@ public final class HdmiCecMessage {
return false;
}
}
+
+ private static boolean isUserControlPressedMessage(int opcode) {
+ return Constants.MESSAGE_USER_CONTROL_PRESSED == opcode;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a086bdaa6ec9..c7d0e9e9f1fe 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -618,6 +618,53 @@ public class HdmiControlService extends SystemService {
initializeCec(INITIATED_BY_ENABLE_CEC);
}
}, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean enabled = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+ == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+ if (isAudioSystemDevice()) {
+ if (audioSystem() == null) {
+ Slog.w(TAG, "Switch device has not registered yet."
+ + " Can't turn routing on.");
+ } else {
+ audioSystem().setRoutingControlFeatureEnabled(enabled);
+ }
+ }
+ }
+ }, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean enabled = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
+ if (isTvDeviceEnabled()) {
+ tv().setSystemAudioControlFeatureEnabled(enabled);
+ }
+ if (isAudioSystemDevice()) {
+ if (audioSystem() == null) {
+ Slog.e(TAG, "Audio System device has not registered yet."
+ + " Can't turn system audio mode on.");
+ } else {
+ audioSystem().onSystemAudioControlFeatureSupportChanged(enabled);
+ }
+ }
+ }
+ }, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
+ }
+ }, mServiceThreadExecutor);
mHdmiCecConfig.registerChangeListener(
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
new HdmiCecConfig.SettingChangeListener() {
@@ -757,11 +804,8 @@ public class HdmiControlService extends SystemService {
private void registerContentObserver() {
ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] {
- Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
- Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED,
- Global.HDMI_CEC_SWITCH_ENABLED,
Global.DEVICE_NAME
};
for (String s : settings) {
@@ -781,33 +825,6 @@ public class HdmiControlService extends SystemService {
String option = uri.getLastPathSegment();
boolean enabled = readBooleanSetting(option, true);
switch (option) {
- case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
- setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
- break;
- case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
- if (isTvDeviceEnabled()) {
- tv().setSystemAudioControlFeatureEnabled(enabled);
- }
- if (isAudioSystemDevice()) {
- if (audioSystem() == null) {
- Slog.e(TAG, "Audio System device has not registered yet."
- + " Can't turn system audio mode on.");
- break;
- }
- audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
- }
- break;
- case Global.HDMI_CEC_SWITCH_ENABLED:
- if (isAudioSystemDevice()) {
- if (audioSystem() == null) {
- Slog.w(TAG, "Switch device has not registered yet."
- + " Can't turn routing on.");
- break;
- }
- audioSystem().setRoutingControlFeatureEnables(enabled);
- }
- break;
case Global.MHL_INPUT_SWITCHING_ENABLED:
setMhlInputChangeEnabled(enabled);
break;
@@ -2359,7 +2376,7 @@ public class HdmiControlService extends SystemService {
@Override
public List<String> getUserCecSettings() {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
} finally {
@@ -2370,7 +2387,7 @@ public class HdmiControlService extends SystemService {
@Override
public List<String> getAllowedCecSettingStringValues(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getAllowedStringValues(name);
} finally {
@@ -2381,7 +2398,7 @@ public class HdmiControlService extends SystemService {
@Override
public int[] getAllowedCecSettingIntValues(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
List<Integer> allowedValues =
HdmiControlService.this.getHdmiCecConfig().getAllowedIntValues(name);
@@ -2394,7 +2411,7 @@ public class HdmiControlService extends SystemService {
@Override
public String getCecSettingStringValue(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getStringValue(name);
} finally {
@@ -2405,7 +2422,7 @@ public class HdmiControlService extends SystemService {
@Override
public void setCecSettingStringValue(String name, String value) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
HdmiControlService.this.getHdmiCecConfig().setStringValue(name, value);
} finally {
@@ -2416,7 +2433,7 @@ public class HdmiControlService extends SystemService {
@Override
public int getCecSettingIntValue(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getIntValue(name);
} finally {
@@ -2427,7 +2444,7 @@ public class HdmiControlService extends SystemService {
@Override
public void setCecSettingIntValue(String name, int value) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
HdmiControlService.this.getHdmiCecConfig().setIntValue(name, value);
} finally {
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 979e7a452e43..3dcf72eb38d6 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -178,10 +178,11 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
if (service.isAudioSystemDevice()) {
return false;
}
- @HdmiControlManager.PowerControlMode String sendStandbyOnSleep =
+ @HdmiControlManager.PowerControlMode String powerControlMode =
service.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+ return powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)
+ || powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index b9404e407b88..0c4fb26ce3b1 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -17,11 +17,10 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.util.Slog;
-import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+import com.android.internal.annotations.VisibleForTesting;
/**
* Feature action for routing control. Exchanges routing-related commands with other devices
@@ -41,23 +40,12 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
// latest routing path to set the new active source.
- private static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
-
- // State in which we wait for <Report Power Status> in response to <Give Device Power Status>
- // we have sent. If the response tells us the device power is on, we send <Set Stream Path>
- // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly
- // just show the blank screen.
- private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
// Time out in millseconds used for <Routing Information>
private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
- // Time out in milliseconds used for <Report Power Status>
- private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000;
-
- // true if <Give Power Status> should be sent once the new active routing path is determined.
- private final boolean mQueryDevicePowerStatus;
-
// If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
// the routing control/active source change happens. The listener should be called if
// the events are triggered by external events such as manual switch port change or incoming
@@ -67,11 +55,9 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiCecLocalDevice localDevice, int path, boolean queryDevicePowerStatus,
- IHdmiControlCallback callback) {
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
super(localDevice, callback);
mCurrentRoutingPath = path;
- mQueryDevicePowerStatus = queryDevicePowerStatus;
// Callback is non-null when routing control action is brought up by binder API. Use
// this as an indicator for the input change notification. These API calls will get
// the result through this callback, not through notification. Any other events that
@@ -104,39 +90,16 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
removeActionExcept(RoutingControlAction.class, this);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
- } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
- && opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
- handleReportPowerStatus(cmd.getParams()[0]);
- return true;
}
return false;
}
- private void handleReportPowerStatus(int devicePowerStatus) {
- if (isPowerOnOrTransient(getTvPowerStatus())) {
- updateActiveInput();
- if (isPowerOnOrTransient(devicePowerStatus)) {
- sendSetStreamPath();
- }
- }
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- }
-
private void updateActiveInput() {
HdmiCecLocalDeviceTv tv = tv();
tv.setPrevPortId(tv.getActivePortId());
tv.updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
}
- private int getTvPowerStatus() {
- return tv().getPowerStatus();
- }
-
- private static boolean isPowerOnOrTransient(int status) {
- return status == HdmiControlManager.POWER_STATUS_ON
- || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
- }
-
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
mCurrentRoutingPath));
@@ -150,46 +113,13 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiDeviceInfo device =
- localDevice().mService.getHdmiCecNetwork().getDeviceInfoByPath(
- mCurrentRoutingPath);
- if (device != null && mQueryDevicePowerStatus) {
- int deviceLogicalAddress = device.getLogicalAddress();
- queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- handlDevicePowerStatusAckResult(
- error == HdmiControlManager.RESULT_SUCCESS);
- }
- });
- } else {
- updateActiveInput();
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- }
- return;
- case STATE_WAIT_FOR_REPORT_POWER_STATUS:
- if (isPowerOnOrTransient(getTvPowerStatus())) {
- updateActiveInput();
- sendSetStreamPath();
- }
+ updateActiveInput();
+ sendSetStreamPath();
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
return;
- }
- }
-
- private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
- callback);
- }
-
- private void handlDevicePowerStatusAckResult(boolean acked) {
- if (acked) {
- mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
- addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
- } else {
- updateActiveInput();
- sendSetStreamPath();
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ default:
+ Slog.e("CEC", "Invalid timeoutState (" + timeoutState + ").");
+ return;
}
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9fcc9a1b7994..d3083ca21f22 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2856,8 +2856,7 @@ public class InputManagerService extends IInputManager.Stub
};
for (File baseDir: baseDirs) {
File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
- try {
- InputStream stream = new FileInputStream(confFile);
+ try (InputStream stream = new FileInputStream(confFile)) {
names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
@@ -2890,8 +2889,7 @@ public class InputManagerService extends IInputManager.Stub
final File baseDir = Environment.getVendorDirectory();
final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
- try {
- final InputStream stream = new FileInputStream(confFile);
+ try (final InputStream stream = new FileInputStream(confFile)) {
return ConfigurationProcessor.processInputPortAssociations(stream);
} catch (FileNotFoundException e) {
// Most of the time, file will not exist, which is expected.
@@ -3012,10 +3010,10 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout) {
- try {
+ try (final InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
result[0] = layout.getDescriptor();
- result[1] = Streams.readFully(new InputStreamReader(
- resources.openRawResource(keyboardLayoutResId)));
+ result[1] = Streams.readFully(stream);
} catch (IOException ex) {
} catch (NotFoundException ex) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fd0f1c38938f..de78becdcee4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -44,7 +44,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FOR
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_IME_WITH_HARD_KEYBOARD;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_REQUESTED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_READY;
-import static android.util.imetracing.ImeTracing.IME_TRACING_FROM_IMMS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
@@ -124,7 +123,6 @@ import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.IWindowManager;
import android.view.InputChannel;
@@ -160,6 +158,7 @@ import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.IVoidResultCallback;
+import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
@@ -2528,9 +2527,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
// Dispatch display id for InputMethodService to update context display.
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
- MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
- mMethodMap.get(mCurMethodId).getConfigChanges()));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_INITIALIZE_IME,
+ mMethodMap.get(mCurMethodId).getConfigChanges(), mCurMethod, mCurToken));
scheduleNotifyImeUidToAudioService(mCurMethodUid);
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
@@ -3928,7 +3926,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void startProtoDump(byte[] protoDump, int source, String where) {
- if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
+ if (protoDump == null && source != ImeTracing.IME_TRACING_FROM_IMMS) {
// Dump not triggered from IMMS, but no proto information provided.
return;
}
@@ -3955,7 +3953,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
proto.end(service_token);
break;
- case IME_TRACING_FROM_IMMS:
+ case ImeTracing.IME_TRACING_FROM_IMMS:
final long managerservice_token =
proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
@@ -4292,12 +4290,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
try {
if (DEBUG) {
Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
- + msg.arg1);
+ + mCurTokenDisplayId);
}
final IBinder token = (IBinder) args.arg2;
- ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
- new InputMethodPrivilegedOperationsImpl(this, token),
- (int) args.arg3);
+ ((IInputMethod) args.arg1).initializeInternal(token,
+ new InputMethodPrivilegedOperationsImpl(this, token), msg.arg1);
} catch (RemoteException e) {
}
args.recycle();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java b/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
deleted file mode 100644
index a6a68934accd..000000000000
--- a/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.inputmethod;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Build;
-import android.os.SystemProperties;
-
-/**
- * Various (pseudo) constants about IME behaviors.
- */
-public class InputMethodSystemProperty {
- /**
- * System property key for the production use. The value must be either empty or a valid
- * (flattened) component name of the multi-client IME.
- */
- private static final String PROP_PROD_MULTI_CLIENT_IME = "ro.sys.multi_client_ime";
-
- /**
- * System property key for debugging purpose. The value must be either empty or a valid
- * (flattened) component name of the multi-client IME.
- *
- * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}</p>
- */
- private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
-
- @Nullable
- private static ComponentName getMultiClientImeComponentName() {
- if (Build.IS_DEBUGGABLE) {
- // If debuggable, allow developers to override the multi-client IME component name
- // with a different (writable) key.
- final ComponentName debugIme = ComponentName.unflattenFromString(
- SystemProperties.get(PROP_DEBUG_MULTI_CLIENT_IME, ""));
- if (debugIme != null) {
- return debugIme;
- }
- }
- return ComponentName.unflattenFromString(
- SystemProperties.get(PROP_PROD_MULTI_CLIENT_IME, ""));
- }
-
- /**
- * {@link ComponentName} of multi-client IME to be used.
- */
- @Nullable
- static final ComponentName sMultiClientImeComponentName = getMultiClientImeComponentName();
-
- /**
- * {@code true} when multi-client IME is enabled.
- */
- public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
-}
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
deleted file mode 100644
index ce195e6ddd64..000000000000
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ /dev/null
@@ -1,1858 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.inputmethod;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.AnyThread;
-import android.annotation.BinderThread;
-import android.annotation.IntDef;
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.annotation.WorkerThread;
-import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.inputmethodservice.MultiClientInputMethodServiceDelegate;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.InputChannel;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.IMultiClientInputMethod;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.inputmethod.StartInputFlags;
-import com.android.internal.inputmethod.StartInputReason;
-import com.android.internal.inputmethod.UnbindReason;
-import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputBindResult;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
-import com.android.server.wm.WindowManagerInternal;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.util.Collections;
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * Actual implementation of multi-client InputMethodManagerService.
- *
- * <p>This system service is intentionally compatible with {@link InputMethodManagerService} so that
- * we can switch the implementation at the boot time.</p>
- */
-public final class MultiClientInputMethodManagerService {
- private static final String TAG = "MultiClientInputMethodManagerService";
- private static final boolean DEBUG = false;
-
- private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE =
- "config_perDisplayFocusEnabled is not true.";
-
- private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG =
- "Consider rebuilding the system image after enabling config_perDisplayFocusEnabled to "
- + "make IME focus compatible with multi-client IME mode.";
-
- private static final long RECONNECT_DELAY_MSEC = 1000;
-
- /**
- * Unlike {@link InputMethodManagerService}, {@link MultiClientInputMethodManagerService}
- * always binds to the IME with {@link Context#BIND_FOREGROUND_SERVICE} for now for simplicity.
- */
- private static final int IME_CONNECTION_UNIFIED_BIND_FLAGS =
- Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_VISIBLE
- | Context.BIND_NOT_FOREGROUND
- | Context.BIND_FOREGROUND_SERVICE;
-
- private static final ComponentName sImeComponentName =
- InputMethodSystemProperty.sMultiClientImeComponentName;
-
- private static void reportNotSupported() {
- if (DEBUG) {
- Slog.d(TAG, "non-supported operation. callers=" + Debug.getCallers(3));
- }
- }
-
- /**
- * {@link MultiClientInputMethodManagerService} is not intended to be instantiated.
- */
- private MultiClientInputMethodManagerService() {
- }
-
- /**
- * The implementation of {@link SystemService} for multi-client IME.
- */
- public static final class Lifecycle extends SystemService {
- private final ApiCallbacks mApiCallbacks;
- private final OnWorkerThreadCallback mOnWorkerThreadCallback;
-
- @MainThread
- public Lifecycle(Context context) {
- super(context);
-
- final UserToInputMethodInfoMap userIdToInputMethodInfoMapper =
- new UserToInputMethodInfoMap();
- final UserDataMap userDataMap = new UserDataMap();
- final HandlerThread workerThread = new HandlerThread(TAG);
- workerThread.start();
- mApiCallbacks = new ApiCallbacks(context, userDataMap, userIdToInputMethodInfoMapper);
- mOnWorkerThreadCallback = new OnWorkerThreadCallback(
- context, userDataMap, userIdToInputMethodInfoMapper,
- new Handler(workerThread.getLooper(), msg -> false, true));
-
- LocalServices.addService(InputMethodManagerInternal.class,
- new InputMethodManagerInternal() {
- @Override
- public void setInteractive(boolean interactive) {
- reportNotSupported();
- }
-
- @Override
- public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
- reportNotSupported();
- }
-
- @Override
- public List<InputMethodInfo> getInputMethodListAsUser(
- @UserIdInt int userId) {
- return userIdToInputMethodInfoMapper.getAsList(userId);
- }
-
- @Override
- public List<InputMethodInfo> getEnabledInputMethodListAsUser(
- @UserIdInt int userId) {
- return userIdToInputMethodInfoMapper.getAsList(userId);
- }
-
- @Override
- public void onCreateInlineSuggestionsRequest(int userId,
- InlineSuggestionsRequestInfo requestInfo,
- IInlineSuggestionsRequestCallback cb) {
- try {
- cb.onInlineSuggestionsUnsupported();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
- }
- }
-
- @Override
- public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
- reportNotSupported();
- return false;
- }
-
- @Override
- public void registerInputMethodListListener(
- InputMethodListListener listener) {
- reportNotSupported();
- }
-
- @Override
- public boolean transferTouchFocusToImeWindow(
- @NonNull IBinder sourceInputToken, int displayId) {
- reportNotSupported();
- return false;
- }
-
- @Override
- public void reportImeControl(@Nullable IBinder windowToken) {
- }
-
- @Override
- public void removeImeSurface() {
- reportNotSupported();
- }
-
- @Override
- public void updateImeWindowStatus(boolean disableImeIcon) {
- }
- });
- }
-
- @MainThread
- @Override
- public void onBootPhase(int phase) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onBootPhase, mOnWorkerThreadCallback, phase));
- }
-
- @MainThread
- @Override
- public void onStart() {
- publishBinderService(Context.INPUT_METHOD_SERVICE, mApiCallbacks);
- }
-
- @MainThread
- @Override
- public void onUserStarting(@NonNull TargetUser user) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onStartUser, mOnWorkerThreadCallback,
- user.getUserIdentifier()));
- }
-
- @MainThread
- @Override
- public void onUserUnlocking(@NonNull TargetUser user) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onUnlockUser, mOnWorkerThreadCallback,
- user.getUserIdentifier()));
- }
-
- @MainThread
- @Override
- public void onUserStopping(@NonNull TargetUser user) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onStopUser, mOnWorkerThreadCallback,
- user.getUserIdentifier()));
- }
- }
-
- private static final class OnWorkerThreadCallback {
- private final Context mContext;
- private final UserDataMap mUserDataMap;
- private final UserToInputMethodInfoMap mInputMethodInfoMap;
- private final Handler mHandler;
-
- OnWorkerThreadCallback(Context context, UserDataMap userDataMap,
- UserToInputMethodInfoMap inputMethodInfoMap, Handler handler) {
- mContext = context;
- mUserDataMap = userDataMap;
- mInputMethodInfoMap = inputMethodInfoMap;
- mHandler = handler;
- }
-
- @AnyThread
- Handler getHandler() {
- return mHandler;
- }
-
- @WorkerThread
- private void tryBindInputMethodService(@UserIdInt int userId) {
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.i(TAG, "tryBindInputMethodService is called for an unknown user=" + userId);
- return;
- }
-
- final InputMethodInfo imi = queryInputMethod(mContext, userId, sImeComponentName);
- if (imi == null) {
- Slog.w(TAG, "Multi-client InputMethod is not found. component="
- + sImeComponentName);
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- case PerUserState.SERVICE_NOT_QUERIED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.UNBIND_CALLED:
- // Safe to clean up.
- mInputMethodInfoMap.remove(userId);
- break;
- }
- }
- return;
- }
-
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- // If the user is still locked, we currently do not try to start IME.
- return;
- case PerUserState.SERVICE_NOT_QUERIED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.UNBIND_CALLED:
- break;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.SERVICE_CONNECTED:
- // OK, nothing to do.
- return;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- data.mState = PerUserState.SERVICE_RECOGNIZED;
- data.mCurrentInputMethodInfo = imi;
- mInputMethodInfoMap.put(userId, imi);
- final boolean bindResult = data.bindServiceLocked(mContext, userId);
- if (!bindResult) {
- Slog.e(TAG, "Failed to bind Multi-client InputMethod.");
- return;
- }
- data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
- }
- }
-
- @WorkerThread
- void onStartUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.v(TAG, "onStartUser userId=" + userId);
- }
- final PerUserData data = new PerUserData(userId, null, PerUserState.USER_LOCKED, this);
- mUserDataMap.put(userId, data);
- }
-
- @WorkerThread
- void onUnlockUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.v(TAG, "onUnlockUser() userId=" + userId);
- }
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.i(TAG, "onUnlockUser is called for an unknown user=" + userId);
- return;
- }
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- data.mState = PerUserState.SERVICE_NOT_QUERIED;
- tryBindInputMethodService(userId);
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- break;
- }
- }
- }
-
- @WorkerThread
- void onStopUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.v(TAG, "onStopUser() userId=" + userId);
- }
- mInputMethodInfoMap.remove(userId);
- final PerUserData data = mUserDataMap.removeReturnOld(userId);
- if (data == null) {
- Slog.i(TAG, "onStopUser is called for an unknown user=" + userId);
- return;
- }
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.UNBIND_CALLED:
- // OK, nothing to do.
- return;
- case PerUserState.SERVICE_CONNECTED:
- case PerUserState.WAITING_SERVICE_CONNECTED:
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- break;
- }
- data.unbindServiceLocked(mContext);
- data.mState = PerUserState.UNBIND_CALLED;
- data.mCurrentInputMethod = null;
-
- // When a Service is explicitly unbound with Context.unbindService(),
- // onServiceDisconnected() will not be triggered. Hence here we explicitly call
- // onInputMethodDisconnectedLocked() as if the Service is already gone.
- data.onInputMethodDisconnectedLocked();
- }
- }
-
- @WorkerThread
- void onServiceConnected(PerUserData data, IMultiClientInputMethod service) {
- if (DEBUG) {
- Slog.v(TAG, "onServiceConnected() data.mUserId=" + data.mUserId);
- }
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.UNBIND_CALLED:
- // We should ignore this callback.
- return;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- // OK.
- data.mState = PerUserState.SERVICE_CONNECTED;
- data.mCurrentInputMethod = service;
- try {
- data.mCurrentInputMethod.initialize(new ImeCallbacks(data));
- } catch (RemoteException e) {
- }
- data.onInputMethodConnectedLocked();
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- }
- }
-
- @WorkerThread
- void onServiceDisconnected(PerUserData data) {
- if (DEBUG) {
- Slog.v(TAG, "onServiceDisconnected() data.mUserId=" + data.mUserId);
- }
- final WindowManagerInternal windowManagerInternal =
- LocalServices.getService(WindowManagerInternal.class);
- synchronized (data.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
- windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
- }
- data.mDisplayIdToImeWindowTokenMap.clear();
- switch (data.mState) {
- case PerUserState.UNBIND_CALLED:
- // We should ignore this callback.
- return;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.SERVICE_CONNECTED:
- // onServiceDisconnected() means the biding is still alive.
- data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
- data.mCurrentInputMethod = null;
- data.onInputMethodDisconnectedLocked();
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- }
- }
-
- @WorkerThread
- void onBindingDied(PerUserData data) {
- if (DEBUG) {
- Slog.v(TAG, "onBindingDied() data.mUserId=" + data.mUserId);
- }
- final WindowManagerInternal windowManagerInternal =
- LocalServices.getService(WindowManagerInternal.class);
- synchronized (data.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
- windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
- }
- data.mDisplayIdToImeWindowTokenMap.clear();
- switch (data.mState) {
- case PerUserState.UNBIND_CALLED:
- // We should ignore this callback.
- return;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.SERVICE_CONNECTED: {
- // onBindingDied() means the biding is dead.
- data.mState = PerUserState.UNBIND_CALLED;
- data.mCurrentInputMethod = null;
- data.onInputMethodDisconnectedLocked();
- // Schedule a retry
- mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::tryBindInputMethodService,
- this, data.mUserId), RECONNECT_DELAY_MSEC);
- break;
- }
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- }
- }
-
- @WorkerThread
- void onBootPhase(int phase) {
- if (DEBUG) {
- Slog.v(TAG, "onBootPhase() phase=" + phase);
- }
- switch (phase) {
- case SystemService.PHASE_ACTIVITY_MANAGER_READY: {
- final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addDataScheme("package");
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onPackageAdded(intent);
- }
- }, filter, null, mHandler);
- break;
- }
- case SystemService.PHASE_BOOT_COMPLETED: {
- final boolean perDisplayFocusEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_perDisplayFocusEnabled);
- if (!perDisplayFocusEnabled) {
- final Bundle extras = new Bundle();
- extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
- mContext.getSystemService(NotificationManager.class).notifyAsUser(TAG,
- SystemMessageProto.SystemMessage.NOTE_SELECT_INPUT_METHOD,
- new Notification.Builder(mContext,
- SystemNotificationChannels.VIRTUAL_KEYBOARD)
- .setContentTitle(PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE)
- .setStyle(new Notification.BigTextStyle()
- .bigText(PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG))
- .setSmallIcon(R.drawable.ic_notification_ime_default)
- .setWhen(0)
- .setOngoing(true)
- .setLocalOnly(true)
- .addExtras(extras)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setColor(mContext.getColor(
- R.color.system_notification_accent_color))
- .build(), UserHandle.ALL);
- }
- break;
- }
- }
- }
-
- @WorkerThread
- void onPackageAdded(Intent intent) {
- if (DEBUG) {
- Slog.v(TAG, "onPackageAdded() intent=" + intent);
- }
- final Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- if (!intent.hasExtra(Intent.EXTRA_UID)) {
- return;
- }
- final String packageName = uri.getSchemeSpecificPart();
- if (sImeComponentName == null
- || packageName == null
- || !TextUtils.equals(sImeComponentName.getPackageName(), packageName)) {
- return;
- }
- final int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, 0));
- tryBindInputMethodService(userId);
- }
- }
-
- private static final class WindowInfo {
- final IBinder mWindowToken;
- final int mWindowHandle;
-
- WindowInfo(IBinder windowToken, int windowCookie) {
- mWindowToken = windowToken;
- mWindowHandle = windowCookie;
- }
- }
-
- /**
- * Describes the state of each IME client.
- */
- @Retention(SOURCE)
- @IntDef({InputMethodClientState.REGISTERED,
- InputMethodClientState.WAITING_FOR_IME_SESSION,
- InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT,
- InputMethodClientState.ALREADY_SENT_BIND_RESULT,
- InputMethodClientState.UNREGISTERED})
- private @interface InputMethodClientState {
- /**
- * {@link IInputMethodManager#addClient(IInputMethodClient, IInputContext, int)} is called
- * and this client is now recognized by the system. When the system lost the connection to
- * the current IME, all the clients need to be re-initialized from this state.
- */
- int REGISTERED = 1;
- /**
- * This client is notified to the current IME with {@link
- * IMultiClientInputMethod#addClient(int, int, int, int)} but the IME is not yet responded
- * with {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
- * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
- */
- int WAITING_FOR_IME_SESSION = 2;
- /**
- * This client is already accepted by the IME but a valid {@link InputBindResult} has not
- * been returned to the client yet.
- */
- int READY_TO_SEND_FIRST_BIND_RESULT = 3;
- /**
- * This client has already received a valid {@link InputBindResult} at least once. This
- * means that the client can directly call {@link IInputMethodSession} IPCs and key events
- * via {@link InputChannel}. When the current IME is unbound, these client end points also
- * need to be cleared.
- */
- int ALREADY_SENT_BIND_RESULT = 4;
- /**
- * The client process is dying.
- */
- int UNREGISTERED = 5;
- }
-
- private static final class InputMethodClientIdSource {
- @GuardedBy("InputMethodClientIdSource.class")
- private static int sNextValue = 0;
-
- private InputMethodClientIdSource() {
- }
-
- static synchronized int getNext() {
- final int result = sNextValue;
- sNextValue++;
- if (sNextValue < 0) {
- sNextValue = 0;
- }
- return result;
- }
- }
-
- private static final class WindowHandleSource {
- @GuardedBy("WindowHandleSource.class")
- private static int sNextValue = 0;
-
- private WindowHandleSource() {
- }
-
- static synchronized int getNext() {
- final int result = sNextValue;
- sNextValue++;
- if (sNextValue < 0) {
- sNextValue = 0;
- }
- return result;
- }
- }
-
- private static final class InputMethodClientInfo {
- final IInputMethodClient mClient;
- final int mUid;
- final int mPid;
- final int mSelfReportedDisplayId;
- final int mClientId;
-
- @GuardedBy("PerUserData.mLock")
- @InputMethodClientState
- int mState;
- @GuardedBy("PerUserData.mLock")
- int mBindingSequence;
- @GuardedBy("PerUserData.mLock")
- InputChannel mWriteChannel;
- @GuardedBy("PerUserData.mLock")
- IInputMethodSession mInputMethodSession;
- @GuardedBy("PerUserData.mLock")
- IMultiClientInputMethodSession mMSInputMethodSession;
- @GuardedBy("PerUserData.mLock")
- final WeakHashMap<IBinder, WindowInfo> mWindowMap = new WeakHashMap<>();
-
- InputMethodClientInfo(IInputMethodClient client, int uid, int pid,
- int selfReportedDisplayId) {
- mClient = client;
- mUid = uid;
- mPid = pid;
- mSelfReportedDisplayId = selfReportedDisplayId;
- mClientId = InputMethodClientIdSource.getNext();
- }
-
- @GuardedBy("PerUserData.mLock")
- void dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- ipw.println("mState=" + mState + ",mBindingSequence=" + mBindingSequence
- + ",mWriteChannel=" + mWriteChannel
- + ",mInputMethodSession=" + mInputMethodSession
- + ",mMSInputMethodSession=" + mMSInputMethodSession);
- }
- }
-
- private static final class UserDataMap {
- @GuardedBy("mMap")
- private final SparseArray<PerUserData> mMap = new SparseArray<>();
-
- @AnyThread
- @Nullable
- PerUserData get(@UserIdInt int userId) {
- synchronized (mMap) {
- return mMap.get(userId);
- }
- }
-
- @AnyThread
- void put(@UserIdInt int userId, PerUserData data) {
- synchronized (mMap) {
- mMap.put(userId, data);
- }
- }
-
- @AnyThread
- @Nullable
- PerUserData removeReturnOld(@UserIdInt int userId) {
- synchronized (mMap) {
- return mMap.removeReturnOld(userId);
- }
- }
-
- @AnyThread
- void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- synchronized (mMap) {
- for (int i = 0; i < mMap.size(); i++) {
- int userId = mMap.keyAt(i);
- PerUserData data = mMap.valueAt(i);
- ipw.println("userId=" + userId + ", data=");
- if (data != null) {
- ipw.increaseIndent();
- data.dump(fd, ipw, args);
- ipw.decreaseIndent();
- }
- }
- }
- }
- }
-
- private static final class TokenInfo {
- final Binder mToken;
- final int mDisplayId;
- TokenInfo(Binder token, int displayId) {
- mToken = token;
- mDisplayId = displayId;
- }
- }
-
- @Retention(SOURCE)
- @IntDef({
- PerUserState.USER_LOCKED,
- PerUserState.SERVICE_NOT_QUERIED,
- PerUserState.SERVICE_RECOGNIZED,
- PerUserState.WAITING_SERVICE_CONNECTED,
- PerUserState.SERVICE_CONNECTED,
- PerUserState.UNBIND_CALLED})
- private @interface PerUserState {
- /**
- * The user is still locked.
- */
- int USER_LOCKED = 1;
- /**
- * The system has not queried whether there is a multi-client IME or not.
- */
- int SERVICE_NOT_QUERIED = 2;
- /**
- * A multi-client IME specified in {@link #PROP_DEBUG_MULTI_CLIENT_IME} is found in the
- * system, but not bound yet.
- */
- int SERVICE_RECOGNIZED = 3;
- /**
- * {@link Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle)} is
- * already called for the IME but
- * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is not yet called
- * back. This includes once the IME is bound but temporarily disconnected as notified with
- * {@link ServiceConnection#onServiceDisconnected(ComponentName)}.
- */
- int WAITING_SERVICE_CONNECTED = 4;
- /**
- * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is already called
- * back. The IME is ready to be used.
- */
- int SERVICE_CONNECTED = 5;
- /**
- * The binding is gone. Either {@link Context#unbindService(ServiceConnection)} is
- * explicitly called or the system decided to destroy the binding as notified with
- * {@link ServiceConnection#onBindingDied(ComponentName)}.
- */
- int UNBIND_CALLED = 6;
- }
-
- /**
- * Takes care of per-user state separation.
- */
- private static final class PerUserData {
- final Object mLock = new Object();
-
- /**
- * User ID (not UID) that is associated with this data.
- */
- @UserIdInt
- private final int mUserId;
-
- /**
- * {@link IMultiClientInputMethod} of the currently connected multi-client IME. This
- * must be non-{@code null} only while {@link #mState} is
- * {@link PerUserState#SERVICE_CONNECTED}.
- */
- @Nullable
- @GuardedBy("mLock")
- IMultiClientInputMethod mCurrentInputMethod;
-
- /**
- * {@link InputMethodInfo} of the currently selected multi-client IME. This must be
- * non-{@code null} unless {@link #mState} is {@link PerUserState#SERVICE_NOT_QUERIED}.
- */
- @GuardedBy("mLock")
- @Nullable
- InputMethodInfo mCurrentInputMethodInfo;
-
- /**
- * Describes the current service state.
- */
- @GuardedBy("mLock")
- @PerUserState
- int mState;
-
- /**
- * A {@link SparseArray} that maps display ID to IME Window token that is already issued to
- * the IME.
- */
- @GuardedBy("mLock")
- final ArraySet<TokenInfo> mDisplayIdToImeWindowTokenMap = new ArraySet<>();
-
- @GuardedBy("mLock")
- private final ArrayMap<IBinder, InputMethodClientInfo> mClientMap = new ArrayMap<>();
-
- @GuardedBy("mLock")
- private SparseArray<InputMethodClientInfo> mClientIdToClientMap = new SparseArray<>();
-
- private final OnWorkerThreadServiceConnection mOnWorkerThreadServiceConnection;
-
- /**
- * A {@link ServiceConnection} that is designed to run on a certain worker thread with
- * which {@link OnWorkerThreadCallback} is associated.
- *
- * @see Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle).
- */
- private static final class OnWorkerThreadServiceConnection implements ServiceConnection {
- private final PerUserData mData;
- private final OnWorkerThreadCallback mCallback;
-
- OnWorkerThreadServiceConnection(PerUserData data, OnWorkerThreadCallback callback) {
- mData = data;
- mCallback = callback;
- }
-
- @WorkerThread
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mCallback.onServiceConnected(mData,
- IMultiClientInputMethod.Stub.asInterface(service));
- }
-
- @WorkerThread
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mCallback.onServiceDisconnected(mData);
- }
-
- @WorkerThread
- @Override
- public void onBindingDied(ComponentName name) {
- mCallback.onBindingDied(mData);
- }
-
- Handler getHandler() {
- return mCallback.getHandler();
- }
- }
-
- PerUserData(@UserIdInt int userId, @Nullable InputMethodInfo inputMethodInfo,
- @PerUserState int initialState, OnWorkerThreadCallback callback) {
- mUserId = userId;
- mCurrentInputMethodInfo = inputMethodInfo;
- mState = initialState;
- mOnWorkerThreadServiceConnection =
- new OnWorkerThreadServiceConnection(this, callback);
- }
-
- @GuardedBy("mLock")
- boolean bindServiceLocked(Context context, @UserIdInt int userId) {
- final Intent intent =
- new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
- .setComponent(mCurrentInputMethodInfo.getComponent())
- .putExtra(Intent.EXTRA_CLIENT_LABEL,
- com.android.internal.R.string.input_method_binding_label)
- .putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- context, 0,
- new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
- PendingIntent.FLAG_MUTABLE));
-
- // Note: Instead of re-dispatching callback from the main thread to the worker thread
- // where OnWorkerThreadCallback is running, we pass the Handler object here so that
- // the callbacks will be directly dispatched to the worker thread.
- return context.bindServiceAsUser(intent, mOnWorkerThreadServiceConnection,
- IME_CONNECTION_UNIFIED_BIND_FLAGS,
- mOnWorkerThreadServiceConnection.getHandler(), UserHandle.of(userId));
- }
-
- @GuardedBy("mLock")
- void unbindServiceLocked(Context context) {
- context.unbindService(mOnWorkerThreadServiceConnection);
- }
-
- @GuardedBy("mLock")
- @Nullable
- InputMethodClientInfo getClientLocked(IInputMethodClient client) {
- return mClientMap.get(client.asBinder());
- }
-
- @GuardedBy("mLock")
- @Nullable
- InputMethodClientInfo getClientFromIdLocked(int clientId) {
- return mClientIdToClientMap.get(clientId);
- }
-
- @GuardedBy("mLock")
- @Nullable
- InputMethodClientInfo removeClientLocked(IInputMethodClient client) {
- final InputMethodClientInfo info = mClientMap.remove(client.asBinder());
- if (info != null) {
- mClientIdToClientMap.remove(info.mClientId);
- }
- return info;
- }
-
- @GuardedBy("mLock")
- void addClientLocked(int uid, int pid, IInputMethodClient client,
- int selfReportedDisplayId) {
- if (getClientLocked(client) != null) {
- Slog.wtf(TAG, "The same client is added multiple times");
- return;
- }
- final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
- try {
- client.asBinder().linkToDeath(deathRecipient, 0);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- final InputMethodClientInfo clientInfo =
- new InputMethodClientInfo(client, uid, pid, selfReportedDisplayId);
- clientInfo.mState = InputMethodClientState.REGISTERED;
- mClientMap.put(client.asBinder(), clientInfo);
- mClientIdToClientMap.put(clientInfo.mClientId, clientInfo);
- switch (mState) {
- case PerUserState.SERVICE_CONNECTED:
- try {
- mCurrentInputMethod.addClient(
- clientInfo.mClientId, clientInfo.mPid, clientInfo.mUid,
- clientInfo.mSelfReportedDisplayId);
- clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
- } catch (RemoteException e) {
- // TODO(yukawa): Need logging and expected behavior
- }
- break;
- }
- }
-
- @GuardedBy("mLock")
- void onInputMethodConnectedLocked() {
- final int numClients = mClientMap.size();
- for (int i = 0; i < numClients; ++i) {
- final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
- switch (clientInfo.mState) {
- case InputMethodClientState.REGISTERED:
- // OK
- break;
- default:
- Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
- return;
- }
- try {
- mCurrentInputMethod.addClient(
- clientInfo.mClientId, clientInfo.mUid, clientInfo.mPid,
- clientInfo.mSelfReportedDisplayId);
- clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
- } catch (RemoteException e) {
- }
- }
- }
-
- @GuardedBy("mLock")
- void onInputMethodDisconnectedLocked() {
- final int numClients = mClientMap.size();
- for (int i = 0; i < numClients; ++i) {
- final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
- switch (clientInfo.mState) {
- case InputMethodClientState.REGISTERED:
- // Disconnected before onInputMethodConnectedLocked().
- break;
- case InputMethodClientState.WAITING_FOR_IME_SESSION:
- // Disconnected between addClient() and acceptClient().
- clientInfo.mState = InputMethodClientState.REGISTERED;
- break;
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- clientInfo.mState = InputMethodClientState.REGISTERED;
- clientInfo.mInputMethodSession = null;
- clientInfo.mMSInputMethodSession = null;
- if (clientInfo.mWriteChannel != null) {
- clientInfo.mWriteChannel.dispose();
- clientInfo.mWriteChannel = null;
- }
- break;
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- try {
- clientInfo.mClient.onUnbindMethod(clientInfo.mBindingSequence,
- UnbindReason.DISCONNECT_IME);
- } catch (RemoteException e) {
- }
- clientInfo.mState = InputMethodClientState.REGISTERED;
- clientInfo.mInputMethodSession = null;
- clientInfo.mMSInputMethodSession = null;
- if (clientInfo.mWriteChannel != null) {
- clientInfo.mWriteChannel.dispose();
- clientInfo.mWriteChannel = null;
- }
- break;
- }
- }
- }
-
- @AnyThread
- void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- synchronized (mLock) {
- ipw.println("mState=" + mState
- + ",mCurrentInputMethod=" + mCurrentInputMethod
- + ",mCurrentInputMethodInfo=" + mCurrentInputMethodInfo);
-
- if (mCurrentInputMethod != null) {
- // indentation will not be kept. So add visual separator here.
- ipw.println(">>Dump CurrentInputMethod>>");
- ipw.flush();
- try {
- TransferPipe.dumpAsync(mCurrentInputMethod.asBinder(), fd, args);
- } catch (IOException | RemoteException e) {
- ipw.println("Failed to dump input method service: " + e);
- }
- ipw.println("<<Dump CurrentInputMethod<<");
- }
-
- ipw.println("mDisplayIdToImeWindowTokenMap=");
- for (TokenInfo info : mDisplayIdToImeWindowTokenMap) {
- ipw.println(" display=" + info.mDisplayId + ",token="
- + info.mToken);
- }
- ipw.println("mClientMap=");
- ipw.increaseIndent();
- for (int i = 0; i < mClientMap.size(); i++) {
-
- ipw.println("binder=" + mClientMap.keyAt(i));
- ipw.println(" InputMethodClientInfo=");
- InputMethodClientInfo info = mClientMap.valueAt(i);
- if (info != null) {
- ipw.increaseIndent();
- info.dumpLocked(fd, ipw, args);
- ipw.decreaseIndent();
- }
- }
- ipw.decreaseIndent();
- ipw.println("mClientIdToClientMap=");
- ipw.increaseIndent();
- for (int i = 0; i < mClientIdToClientMap.size(); i++) {
- ipw.println("clientId=" + mClientIdToClientMap.keyAt(i));
- ipw.println(" InputMethodClientInfo=");
- InputMethodClientInfo info = mClientIdToClientMap.valueAt(i);
- if (info != null) {
- ipw.increaseIndent();
- info.dumpLocked(fd, ipw, args);
- ipw.decreaseIndent();
- }
- if (info.mClient != null) {
- // indentation will not be kept. So add visual separator here.
- ipw.println(">>DumpClientStart>>");
- ipw.flush(); // all writes should be flushed to guarantee order.
- try {
- TransferPipe.dumpAsync(info.mClient.asBinder(), fd, args);
- } catch (IOException | RemoteException e) {
- ipw.println(" Failed to dump client:" + e);
- }
- ipw.println("<<DumpClientEnd<<");
- }
- }
- ipw.decreaseIndent();
- }
- }
-
- private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
- private final PerUserData mPerUserData;
- private final IInputMethodClient mClient;
-
- ClientDeathRecipient(PerUserData perUserData, IInputMethodClient client) {
- mPerUserData = perUserData;
- mClient = client;
- }
-
- @BinderThread
- @Override
- public void binderDied() {
- synchronized (mPerUserData.mLock) {
- mClient.asBinder().unlinkToDeath(this, 0);
-
- final InputMethodClientInfo clientInfo =
- mPerUserData.removeClientLocked(mClient);
- if (clientInfo == null) {
- return;
- }
-
- if (clientInfo.mWriteChannel != null) {
- clientInfo.mWriteChannel.dispose();
- clientInfo.mWriteChannel = null;
- }
- if (clientInfo.mInputMethodSession != null) {
- try {
- clientInfo.mInputMethodSession.finishSession();
- } catch (RemoteException e) {
- }
- clientInfo.mInputMethodSession = null;
- }
- clientInfo.mMSInputMethodSession = null;
- clientInfo.mState = InputMethodClientState.UNREGISTERED;
- switch (mPerUserData.mState) {
- case PerUserState.SERVICE_CONNECTED:
- try {
- mPerUserData.mCurrentInputMethod.removeClient(clientInfo.mClientId);
- } catch (RemoteException e) {
- // TODO(yukawa): Need logging and expected behavior
- }
- break;
- }
- }
- }
- }
- }
-
- /**
- * Queries for multi-client IME specified with {@code componentName}.
- *
- * @param context {@link Context} to be used to query component.
- * @param userId User ID for which the multi-client IME is queried.
- * @param componentName {@link ComponentName} to be queried.
- * @return {@link InputMethodInfo} when multi-client IME is found. Otherwise {@code null}.
- */
- @Nullable
- private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
- @Nullable ComponentName componentName) {
- if (componentName == null) {
- Slog.w(TAG, "queryInputMethod invoked with null componentName");
- return null;
- }
-
- // Use for queryIntentServicesAsUser
- final PackageManager pm = context.getPackageManager();
- final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
- new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
- .setComponent(componentName),
- PackageManager.GET_META_DATA, userId);
- try {
- return new InputMethodInfo(context, resolveMultiClientImeService(services));
- } catch (Exception e) {
- Slog.wtf(TAG, "Unable to load input method from services (" + services + ")", e);
- }
- return null;
- }
-
- /**
- * Determines the multi-client IME from the specified {@link List<ResolveInfo>}.
- *
- * @return {@link ResolveInfo} when an appropriate multi-client IME is found.
- * Otherwise {@code null}.
- */
- @Nullable
- @VisibleForTesting
- static ResolveInfo resolveMultiClientImeService(@NonNull List<ResolveInfo> services) {
- if (services.isEmpty()) {
- Slog.e(TAG, "No IME found");
- return null;
- }
- if (services.size() > 1) {
- Slog.e(TAG, "Only one IME service is supported.");
- return null;
- }
- final ResolveInfo ri = services.get(0);
- ServiceInfo si = ri.serviceInfo;
- final String imeId = InputMethodInfo.computeId(ri);
- if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
- Slog.e(TAG, imeId + " must have required"
- + android.Manifest.permission.BIND_INPUT_METHOD);
- return null;
- }
- if (!Build.IS_DEBUGGABLE && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
- return null;
- }
- return ri;
- }
-
- /**
- * Manages the mapping rule from user ID to {@link InputMethodInfo}.
- */
- private static final class UserToInputMethodInfoMap {
- @GuardedBy("mArray")
- private final SparseArray<InputMethodInfo> mArray = new SparseArray<>();
-
- @AnyThread
- void put(@UserIdInt int userId, InputMethodInfo imi) {
- synchronized (mArray) {
- mArray.put(userId, imi);
- }
- }
-
- @AnyThread
- void remove(@UserIdInt int userId) {
- synchronized (mArray) {
- mArray.remove(userId);
- }
- }
-
- @AnyThread
- @Nullable
- InputMethodInfo get(@UserIdInt int userId) {
- synchronized (mArray) {
- return mArray.get(userId);
- }
- }
-
- @AnyThread
- List<InputMethodInfo> getAsList(@UserIdInt int userId) {
- final InputMethodInfo info = get(userId);
- if (info == null) {
- return Collections.emptyList();
- }
- return Collections.singletonList(info);
- }
-
- @AnyThread
- void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- synchronized (mArray) {
- for (int i = 0; i < mArray.size(); i++) {
- ipw.println("userId=" + mArray.keyAt(i));
- ipw.println(" InputMethodInfo=" + mArray.valueAt(i));
- }
- }
- }
- }
-
- /**
- * Takes care of IPCs exposed to the multi-client IME.
- */
- private static final class ImeCallbacks
- extends IMultiClientInputMethodPrivilegedOperations.Stub {
- private final PerUserData mPerUserData;
- private final WindowManagerInternal mIWindowManagerInternal;
-
- ImeCallbacks(PerUserData perUserData) {
- mPerUserData = perUserData;
- mIWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
- }
-
- @BinderThread
- @Override
- public IBinder createInputMethodWindowToken(int displayId) {
- synchronized (mPerUserData.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo tokenInfo =
- mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
- // Currently we issue up to one window token per display.
- if (tokenInfo.mDisplayId == displayId) {
- return tokenInfo.mToken;
- }
- }
-
- final Binder token = new Binder();
- Binder.withCleanCallingIdentity(
- PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
- mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId,
- null /* options */));
- mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
- return token;
- }
- }
-
- @BinderThread
- @Override
- public void deleteInputMethodWindowToken(IBinder token) {
- synchronized (mPerUserData.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo tokenInfo =
- mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
- if (tokenInfo.mToken == token) {
- mPerUserData.mDisplayIdToImeWindowTokenMap.remove(tokenInfo);
- break;
- }
- }
- }
- }
-
- @BinderThread
- @Override
- public void acceptClient(int clientId, IInputMethodSession inputMethodSession,
- IMultiClientInputMethodSession multiSessionInputMethodSession,
- InputChannel writeChannel) {
- synchronized (mPerUserData.mLock) {
- final InputMethodClientInfo clientInfo =
- mPerUserData.getClientFromIdLocked(clientId);
- if (clientInfo == null) {
- Slog.e(TAG, "Unknown clientId=" + clientId);
- return;
- }
- switch (clientInfo.mState) {
- case InputMethodClientState.WAITING_FOR_IME_SESSION:
- try {
- clientInfo.mClient.setActive(true, false, false);
- } catch (RemoteException e) {
- // TODO(yukawa): Remove this client.
- return;
- }
- clientInfo.mState = InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT;
- clientInfo.mWriteChannel = writeChannel;
- clientInfo.mInputMethodSession = inputMethodSession;
- clientInfo.mMSInputMethodSession = multiSessionInputMethodSession;
- break;
- default:
- Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
- break;
- }
- }
- }
-
- @BinderThread
- @Override
- public void reportImeWindowTarget(int clientId, int targetWindowHandle,
- IBinder imeWindowToken) {
- synchronized (mPerUserData.mLock) {
- final InputMethodClientInfo clientInfo =
- mPerUserData.getClientFromIdLocked(clientId);
- if (clientInfo == null) {
- Slog.e(TAG, "Unknown clientId=" + clientId);
- return;
- }
- for (WindowInfo windowInfo : clientInfo.mWindowMap.values()) {
- if (windowInfo.mWindowHandle == targetWindowHandle) {
- final IBinder targetWindowToken = windowInfo.mWindowToken;
- if (DEBUG) {
- Slog.v(TAG, "reportImeWindowTarget"
- + " clientId=" + clientId
- + " imeWindowToken=" + imeWindowToken
- + " targetWindowToken=" + targetWindowToken);
- }
- mIWindowManagerInternal.updateInputMethodTargetWindow(
- imeWindowToken, targetWindowToken);
- }
- }
- // not found.
- }
- }
-
- @BinderThread
- @Override
- public boolean isUidAllowedOnDisplay(int displayId, int uid) {
- return mIWindowManagerInternal.isUidAllowedOnDisplay(displayId, uid);
- }
-
- @BinderThread
- @Override
- public void setActive(int clientId, boolean active) {
- synchronized (mPerUserData.mLock) {
- final InputMethodClientInfo clientInfo =
- mPerUserData.getClientFromIdLocked(clientId);
- if (clientInfo == null) {
- Slog.e(TAG, "Unknown clientId=" + clientId);
- return;
- }
- try {
- clientInfo.mClient.setActive(active, false /* fullscreen */, false);
- } catch (RemoteException e) {
- return;
- }
- }
- }
- }
-
- /**
- * Takes care of IPCs exposed to the IME client.
- */
- private static final class ApiCallbacks extends IInputMethodManager.Stub {
- private final Context mContext;
- private final UserDataMap mUserDataMap;
- private final UserToInputMethodInfoMap mInputMethodInfoMap;
- private final AppOpsManager mAppOpsManager;
- private final WindowManagerInternal mWindowManagerInternal;
-
- ApiCallbacks(Context context, UserDataMap userDataMap,
- UserToInputMethodInfoMap inputMethodInfoMap) {
- mContext = context;
- mUserDataMap = userDataMap;
- mInputMethodInfoMap = inputMethodInfoMap;
- mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
- }
-
- @AnyThread
- private boolean checkFocus(int uid, int pid, int displayId) {
- return mWindowManagerInternal.isInputMethodClientFocus(uid, pid, displayId);
- }
-
- @BinderThread
- @Override
- public void addClient(IInputMethodClient client, IInputContext inputContext,
- int selfReportedDisplayId) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "addClient() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return;
- }
- synchronized (data.mLock) {
- data.addClientLocked(callingUid, callingPid, client, selfReportedDisplayId);
- }
- }
-
- @BinderThread
- @Override
- public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
- }
- return mInputMethodInfoMap.getAsList(userId);
- }
-
- @BinderThread
- @Override
- public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
- }
- return mInputMethodInfoMap.getAsList(userId);
- }
-
- @BinderThread
- @Override
- public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
- boolean allowsImplicitlySelectedSubtypes) {
- reportNotSupported();
- return Collections.emptyList();
- }
-
- @BinderThread
- @Override
- public InputMethodSubtype getLastInputMethodSubtype() {
- reportNotSupported();
- return null;
- }
-
- @BinderThread
- @Override
- public void removeImeSurface() {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public boolean showSoftInput(
- IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- return showSoftInputInternal(client, token, flags, resultReceiver);
- }
-
- @BinderThread
- private boolean showSoftInputInternal(
- IInputMethodClient client, IBinder token, int flags,
- ResultReceiver resultReceiver) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "showSoftInput() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return false;
- }
- synchronized (data.mLock) {
- final InputMethodClientInfo clientInfo = data.getClientLocked(client);
- if (clientInfo == null) {
- Slog.e(TAG, "showSoftInput. client not found. ignoring.");
- return false;
- }
- if (clientInfo.mUid != callingUid) {
- Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
- + " actual=" + callingUid);
- return false;
- }
- switch (clientInfo.mState) {
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- try {
- clientInfo.mMSInputMethodSession.showSoftInput(flags, resultReceiver);
-
- // Forcing WM to show IME on imeTargetWindow
- mWindowManagerInternal.showImePostLayout(token);
- } catch (RemoteException e) {
- }
- break;
- default:
- if (DEBUG) {
- Slog.e(TAG, "Ignoring showSoftInput(). clientState="
- + clientInfo.mState);
- }
- break;
- }
- return true;
- }
- }
-
- @BinderThread
- @Override
- public boolean hideSoftInput(
- IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- return hideSoftInputInternal(client, windowToken, flags, resultReceiver);
- }
-
- @BinderThread
- private boolean hideSoftInputInternal(
- IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "hideSoftInput() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return false;
- }
- synchronized (data.mLock) {
- final InputMethodClientInfo clientInfo = data.getClientLocked(client);
- if (clientInfo == null) {
- return false;
- }
- if (clientInfo.mUid != callingUid) {
- Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
- + " actual=" + callingUid);
- return false;
- }
- switch (clientInfo.mState) {
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- try {
- clientInfo.mMSInputMethodSession.hideSoftInput(flags, resultReceiver);
- } catch (RemoteException e) {
- }
- break;
- default:
- if (DEBUG) {
- Slog.e(TAG, "Ignoring hideSoftInput(). clientState="
- + clientInfo.mState);
- }
- break;
- }
- return true;
- }
- }
-
- @BinderThread
- @Override
- public InputBindResult startInputOrWindowGainedFocus(
- @StartInputReason int startInputReason,
- @Nullable IInputMethodClient client,
- @Nullable IBinder windowToken,
- @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode,
- int windowFlags,
- @Nullable EditorInfo editorInfo,
- @Nullable IInputContext inputContext,
- @MissingMethodFlags int missingMethods,
- int unverifiedTargetSdkVersion) {
- return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
- startInputFlags, softInputMode, windowFlags, editorInfo, inputContext,
- missingMethods, unverifiedTargetSdkVersion);
- }
-
- @BinderThread
- private InputBindResult startInputOrWindowGainedFocusInternal(
- @StartInputReason int startInputReason,
- @Nullable IInputMethodClient client,
- @Nullable IBinder windowToken,
- @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode,
- int windowFlags,
- @Nullable EditorInfo editorInfo,
- @Nullable IInputContext inputContext,
- @MissingMethodFlags int missingMethods,
- int unverifiedTargetSdkVersion) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
-
- if (client == null) {
- return InputBindResult.INVALID_CLIENT;
- }
-
- final boolean packageNameVerified =
- editorInfo != null && InputMethodUtils.checkIfPackageBelongsToUid(
- mAppOpsManager, callingUid, editorInfo.packageName);
- if (editorInfo != null && !packageNameVerified) {
- Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
- + " uid=" + callingUid + " package=" + editorInfo.packageName);
- return InputBindResult.INVALID_PACKAGE_NAME;
- }
-
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "startInputOrWindowGainedFocus() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return InputBindResult.INVALID_USER;
- }
-
- synchronized (data.mLock) {
- final InputMethodClientInfo clientInfo = data.getClientLocked(client);
- if (clientInfo == null) {
- return InputBindResult.INVALID_CLIENT;
- }
- if (clientInfo.mUid != callingUid) {
- Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
- + " actual=" + callingUid);
- return InputBindResult.INVALID_CLIENT;
- }
-
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- case PerUserState.SERVICE_NOT_QUERIED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.UNBIND_CALLED:
- return InputBindResult.IME_NOT_CONNECTED;
- case PerUserState.SERVICE_CONNECTED:
- // OK
- break;
- default:
- Slog.wtf(TAG, "Unexpected state=" + data.mState);
- return InputBindResult.IME_NOT_CONNECTED;
- }
-
- WindowInfo windowInfo = null;
- if (windowToken != null) {
- windowInfo = clientInfo.mWindowMap.get(windowToken);
- if (windowInfo == null) {
- windowInfo = new WindowInfo(windowToken, WindowHandleSource.getNext());
- clientInfo.mWindowMap.put(windowToken, windowInfo);
- }
- }
-
- if (!checkFocus(clientInfo.mUid, clientInfo.mPid,
- clientInfo.mSelfReportedDisplayId)) {
- return InputBindResult.NOT_IME_TARGET_WINDOW;
- }
-
- if (editorInfo == null) {
- // So-called fallback InputConnection scenario. For app compatibility, we still
- // notify this to the IME.
- switch (clientInfo.mState) {
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- final int windowHandle = windowInfo != null
- ? windowInfo.mWindowHandle
- : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
- try {
- clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
- inputContext, missingMethods, editorInfo, startInputFlags,
- softInputMode, windowHandle);
- } catch (RemoteException ignored) { }
- break;
- }
- return InputBindResult.NULL_EDITOR_INFO;
- }
-
- switch (clientInfo.mState) {
- case InputMethodClientState.REGISTERED:
- case InputMethodClientState.WAITING_FOR_IME_SESSION:
- clientInfo.mBindingSequence++;
- if (clientInfo.mBindingSequence < 0) {
- clientInfo.mBindingSequence = 0;
- }
- return new InputBindResult(
- InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, false);
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- clientInfo.mBindingSequence++;
- if (clientInfo.mBindingSequence < 0) {
- clientInfo.mBindingSequence = 0;
- }
- // Successful start input.
- final int windowHandle = windowInfo != null
- ? windowInfo.mWindowHandle
- : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
- try {
- clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
- inputContext, missingMethods, editorInfo, startInputFlags,
- softInputMode, windowHandle);
- } catch (RemoteException ignored) { }
- clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
- return new InputBindResult(
- InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
- clientInfo.mInputMethodSession,
- clientInfo.mWriteChannel.dup(),
- data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, false);
- case InputMethodClientState.UNREGISTERED:
- Slog.e(TAG, "The client is already unregistered.");
- return InputBindResult.INVALID_CLIENT;
- }
- }
- return null;
- }
-
- @BinderThread
- @Override
- public void showInputMethodPickerFromClient(IInputMethodClient client,
- int auxiliarySubtypeMode) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void showInputMethodPickerFromSystem(IInputMethodClient client,
- int auxiliarySubtypeMode, int displayId) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient client,
- String inputMethodId) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public boolean isInputMethodPickerShownForTest() {
- reportNotSupported();
- return false;
- }
-
- @BinderThread
- @Override
- public InputMethodSubtype getCurrentInputMethodSubtype() {
- reportNotSupported();
- return null;
- }
-
- @BinderThread
- @Override
- public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public int getInputMethodWindowVisibleHeight() {
- reportNotSupported();
- return 0;
- }
-
- @BinderThread
- @Override
- public void reportPerceptibleAsync(IBinder windowClient, boolean perceptible) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
- @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
- ResultReceiver resultReceiver) {
- }
-
- @BinderThread
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- final String prefixChild = " ";
- pw.println("Current Multi Client Input Method Manager state:");
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println("mUserDataMap=");
- if (mUserDataMap != null) {
- ipw.increaseIndent();
- mUserDataMap.dump(fd, ipw, args);
- }
- }
-
- @BinderThread
- @Override
- public void startProtoDump(byte[] clientProtoDump, int source, String where) {
- }
-
- @BinderThread
- @Override
- public boolean isImeTraceEnabled() {
- return false;
- }
-
- @BinderThread
- @Override
- public void startImeTrace() {
- }
-
- @BinderThread
- @Override
- public void stopImeTrace() {
- }
- }
-}
diff --git a/services/core/java/com/android/server/inputmethod/multi-client-ime.md b/services/core/java/com/android/server/inputmethod/multi-client-ime.md
deleted file mode 100644
index ba92cd2d4690..000000000000
--- a/services/core/java/com/android/server/inputmethod/multi-client-ime.md
+++ /dev/null
@@ -1,194 +0,0 @@
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-# Multi Client Input Method Editors
-
-## History of Multi Client Input Method Editors (Multi Client IMEs)
-
-An advanced multi-display support is requested for certain Android form-factors so that user(s) can type text on each display at the same time without losing software keyboard focus in other displays (hereafter called "multi-client scenario"). This is not possible in Android IMEs built on top of `InputMethodService` class. The assumption that a single IME client can be focused at the same time was made before Android IME APIs were introduced in Android 1.5 and many public APIs in `InputMethodService` have already relied heavily on that assumption (hereafter called "single-client scenario"). Updating `InputMethodService` class to support multi-client scenario is, however, quite challenging because:
-
- 1. doing so would introduce an unacceptable amount of complexity into `InputMethodService`, which is already hard to maintain,
- 2. IME developers still need to update their implementation to be able to support parallel requests from multiple focused IME client, which may require non-trivial redesign in their side (e.g. input decoder, typing history database, ...), and
- 3. actual use cases for multi IME clients are expected to be evolved rapidly hence the new protocol is not yet stable and not yet ready to be exposed as public APIs.
-
-Thus the first decision we made was that to support such special multi-display environments a new type of IME (hereafter called "multi-client IME") needs to be designed and implemented rather than reusing `InputMethodService` public class. On top of this decision, following decisions were also made:
-
- * Multi-client IME V1 will be built on top of private APIs. This means:
- * Multi-client IME must be pre-installed into the system. They cannot be distributed via application store since protocol compatibility is not guaranteed across devices and releases.
- * The system should trust multi-client IME to some extent. System integrators are responsible for making sure that the pre-installed multi-client IME works as expected.
- * Unlike `InputMethodService`, multiple multi-client IMEs cannot be enabled. The system pre-installs only one multi-client IME.
- * Punt some special features of Android IMEs (e.g. fullscreen mode, InputMethodSubtype, ...) from V1 goal unless someone actually requests those features for multi-client IME scenario.
- * Introduce `MultiClientInputMethodManagerService` (MCIMMS) for multi-client IME scenario and use it instead of `InputMethodManagerService` (IMMS) when a certain runtime flag is enabled at the device boot time. This means:
- * basically no risk for single-client scenario,
- * the feature can be easily deprecated, and
- * it forces us to rewrite IME system server, which is expected to be a good chance to reconsider what Android IME protocol should look like.
- * Most of form-factors such as Phones and TVs continue to use IMMS and support at most one focused IME client even under multi-display environment.
-
-
-## How to test
-
-For multi-client IME to properly work, an internal boolean resource `com.android.internal.R.bool.config_perDisplayFocusEnabled` needs to be `true`. Since this value cannot be overridden at the run time, you may need to rebuild the system image to enable per-display focus mode.
-
-As for multi-client IME mode itself, you can enable multi-client IME mode just by setting a valid component name that supports multi-client IME protocol to the system property `persist.debug.multi_client_ime`, as long as `android.os.Build.IS_DEBUGGABLE` returns `true` and you can have root access. Reboot is required for this to take effect.
-
-```shell
-# Build and install a sample multi-client IME
-make -j MultiClientInputMethod
-adb install -r $OUT/system/priv-app/MultiClientInputMethod/MultiClientInputMethod.apk
-
-# Enable multi-client IME for the side-loaded sample multi-client IME
-adb root
-adb shell setprop persist.debug.multi_client_ime com.example.android.multiclientinputmethod/.MultiClientInputMethod
-adb reboot
-```
-
-To disable multi-client IME on non-supported devices again, just clear `persist.debug.multi_client_ime` as follows. Reboot is still required for this to take effect.
-
-```shell
-# Disable multi-client IME again
-adb root
-adb shell "setprop persist.debug.multi_client_ime ''"
-adb reboot
-```
-
-## How to develop multi-client IMEs
-
-There is a sample multi-client IME in `development/samples/MultiClientInputMethod/`.
-
-## Versioning
-
-Neither forward nor backward compatibility is guaranteed in multi-client IME APIs. The system integrator is responsible for making sure that both the system and pre-installed multi-client IME are compatible with each other every time the system image is updated.
-
-## Implementation note
-
-### Unsupported features
-
- * VR IME
- * `VrManager#setVrInputMethod()` system API is not supported.
- * InputMethodSubtype
- * Following APIs are not supported
- * `InputMethodManager#getEnabledInputMethodSubtypeList()`
- * `InputMethodManager#getCurrentInputMethodSubtype()`
- * `InputMethodManager#setCurrentInputMethodSubtype()`
- * `InputMethodManager#getShortcutInputMethodsAndSubtypes()`
- * `InputMethodManager#setAdditionalInputMethodSubtypes()`
- * `InputMethodManager#getLastInputMethodSubtype()`
- * `Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE`
- * IME switching
- * Following APIs are not supported
- * `InputMethodManager#showInputMethodPicker()`
- * `InputMethodManager#showInputMethodAndSubtypeEnabler()`
- * `InputMethodManager#setInputMethod()`
- * `InputMethodManager#setInputMethodAndSubtype()`
- * `InputMethodManager#switchToLastInputMethod()`
- * `InputMethodManager#switchToNextInputMethod()`
- * `InputMethodManager#shouldOfferSwitchingToNextInputMethod()`
- * `Settings.Secure#DEFAULT_INPUT_METHOD`
- * `Settings.Secure#ENABLED_INPUT_METHODS`
- * Direct-boot aware multi-client IME
- * Device manufacturer can work around this by integrating in-app keyboard into the initial unlock screen.
- * Full-screen mode
- * Following API always returns `false`.
- * `InputMethodManager#isFullscreenMode()`
- * Custom inset
- * For instance, floating IME cannot be implemented right now.
- * Custom touchable region (`InputMethodService.Insets#touchableRegion`)
- * Image Insertion API
- * `InputConnection#commitContent()` API is silently ignored.
- * `adb shell dumpsys` does not include any log from MCIMMS yet.
-
-### Security
-
-#### Root permission is required to enable MCIMMS on non-supported devices
-
-In order to override `persist.debug.multi_client_ime` device property, an explicit root permission is needed.
-
-#### Multi-client IME must be pre-installed
-
-Multi-client IME must be pre-installed since it is considered as part of the system component. This is verified by checking `ApplicationInfo.FLAG_SYSTEM` bit. This security check can be bypassed when `Build.IS_DEBUGGABLE` is `true` so that IME developers can easily side-load their APKs during development phase.
-
-```java
-public final class MultiClientInputMethodManagerService {
- ...
- @Nullable
- private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
- @Nullable ComponentName componentName) {
-
- ...
-
- if (! && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
- return null;
- }
-```
-[services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java](MultiClientInputMethodManagerService.java)
-
-
-#### Integer handle vs IBinder token
-
-Sometimes MCIMMS needs to issue certain types of identifiers to the multi-client IME so that the IME can later specify to which entity or resource it intends to access. A good example is the IME client identifier. Multi-client IME definitely need to be able to specify which IME client to be interacted with for certain operations. The problem is that MCIMMS cannot simply pass `IInputMethodClient` to the multi-client IME as an ID because it would allow the IME to make IPC calls to the IME client. For this kind of situations, we usually use `Binder` object just as a non-spoofable token. For instance, IMMS creates another 'Binder' token then pass it to the IME, instead of directly passing 'IWindow' Binder token.
-
-```java
-public class InputMethodManagerService extends IInputMethodManager.Stub
- implements ServiceConnection, Handler.Callback {
- ...
- @GuardedBy("mMethodMap")
- private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
-
- ...
-
- @GuardedBy("mMethodMap")
- @NonNull
- InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
- ...
- final Binder startInputToken = new Binder();
- final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
- !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
- mCurSeq);
- mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
- ...
- }
-
- ...
-
- @BinderThread
- private void reportStartInput(IBinder token, IBinder startInputToken) {
- if (!calledWithValidToken(token)) {
- return;
- }
-
- synchronized (mMethodMap) {
- final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
- if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
- mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
- }
- mLastImeTargetWindow = targetWindow;
- }
- }
-```
-[services/core/java/com/android/server/inputmethod/InputMethodManagerService.java](InputMethodManagerService.java)
-
-However, in MCIMMS, for certain cases we decided to use a simple integer token, which can be spoofable and can be messed up if integer overflow happens. This is because:
-
- * It does not make much sense to worry about malicious multi-client IMEs, because it is guaranteed to be a pre-installed system component.
- * Integer token is expected to be a more lightweight that `Binder` token.
- * For that use case, integer overflow is unrealistic.
- * Strict user separation is still enforced. Multi-client IMEs are still not allowed to interact with other users' resources by any means.
-
-Currently the following IDs are implemented as integer tokens:
-
- * Client ID
- * Window Handle
- * Note that each IME client has its own Window Handle mapping table. Window Handle is valid only within the associated IME client.
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index 1967e026e46c..e3750074168c 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -82,7 +82,7 @@ public class GnssAntennaInfoProvider extends
}
public void addListener(CallerIdentity callerIdentity, IGnssAntennaInfoListener listener) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
putRegistration(listener.asBinder(),
new AntennaInfoListenerRegistration(callerIdentity, listener));
@@ -92,7 +92,7 @@ public class GnssAntennaInfoProvider extends
}
public void removeListener(IGnssAntennaInfoListener listener) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
removeRegistration(listener.asBinder());
} finally {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index f3dcfbbf2c0a..206682e46d47 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -68,8 +68,6 @@ import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -138,13 +136,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
- // handler messages
- private static final int INJECT_NTP_TIME = 5;
- private static final int DOWNLOAD_PSDS_DATA = 6;
- private static final int REQUEST_LOCATION = 16;
- private static final int REPORT_LOCATION = 17; // HAL reports location
- private static final int REPORT_SV_STATUS = 18; // HAL reports SV status
-
// TCP/IP constants.
// Valid TCP/UDP port range is (0, 65535].
private static final int TCP_MIN_PORT = 0;
@@ -402,7 +393,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
BatteryStats.SERVICE_NAME));
// Construct internal handler
- mHandler = new ProviderHandler(FgThread.getHandler().getLooper());
+ mHandler = FgThread.getHandler();
// Load GPS configuration and register listeners in the background:
// some operations, such as opening files and registering broadcast receivers, can take a
@@ -530,7 +521,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (mSupportsPsds) {
synchronized (mLock) {
for (int psdsType : mPendingDownloadPsdsTypes) {
- sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+ postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
}
mPendingDownloadPsdsTypes.clear();
}
@@ -654,9 +645,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
synchronized (mLock) {
backoffMillis = mPsdsBackOff.nextBackoffMillis();
}
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 0, null),
- backoffMillis);
+ mHandler.postDelayed(() -> handleDownloadPsdsData(psdsType), backoffMillis);
}
// Release wake lock held by task, synchronize on mLock in case multiple
@@ -976,8 +965,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
requestUtcTime();
} else if ("force_psds_injection".equals(command)) {
if (mSupportsPsds) {
- sendMessage(DOWNLOAD_PSDS_DATA, GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX,
- null);
+ postWithWakeLockHeld(() -> handleDownloadPsdsData(
+ GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
}
} else if ("request_power_stats".equals(command)) {
mGnssNative.requestPowerStats();
@@ -1314,7 +1303,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private void requestUtcTime() {
if (DEBUG) Log.d(TAG, "utcTimeRequest");
- sendMessage(INJECT_NTP_TIME, 0, null);
+ postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
}
private void requestRefLocation() {
@@ -1348,75 +1337,20 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
}
- boolean isInEmergencySession() {
- return mNIHandler.getInEmergency();
- }
-
- private void sendMessage(int message, int arg, Object obj) {
+ private void postWithWakeLockHeld(Runnable runnable) {
// hold a wake lock until this message is delivered
// note that this assumes the message will not be removed from the queue before
// it is handled (otherwise the wake lock would be leaked).
mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
- if (DEBUG) {
- Log.d(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
- + ", " + obj + ")");
- }
- mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
- }
-
- private final class ProviderHandler extends Handler {
- ProviderHandler(Looper looper) {
- super(looper, null, true /*async*/);
- }
-
- @Override
- public void handleMessage(Message msg) {
- int message = msg.what;
- switch (message) {
- case INJECT_NTP_TIME:
- mNtpTimeHelper.retrieveAndInjectNtpTime();
- break;
- case REQUEST_LOCATION:
- handleRequestLocation(msg.arg1 == 1, (boolean) msg.obj);
- break;
- case DOWNLOAD_PSDS_DATA:
- handleDownloadPsdsData(msg.arg1);
- break;
- case REPORT_LOCATION:
- handleReportLocation(msg.arg1 == 1, (Location) msg.obj);
- break;
- case REPORT_SV_STATUS:
- handleReportSvStatus((GnssStatus) msg.obj);
- break;
- }
- if (msg.arg2 == 1) {
- // wakelock was taken for this message, release it
+ boolean success = mHandler.post(() -> {
+ try {
+ runnable.run();
+ } finally {
mWakeLock.release();
- if (DEBUG) {
- Log.d(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
- + ", " + msg.arg1 + ", " + msg.obj + ")");
- }
}
- }
- }
-
- /**
- * @return A string representing the given message ID.
- */
- private String messageIdAsString(int message) {
- switch (message) {
- case INJECT_NTP_TIME:
- return "INJECT_NTP_TIME";
- case REQUEST_LOCATION:
- return "REQUEST_LOCATION";
- case DOWNLOAD_PSDS_DATA:
- return "DOWNLOAD_PSDS_DATA";
- case REPORT_LOCATION:
- return "REPORT_LOCATION";
- case REPORT_SV_STATUS:
- return "REPORT_SV_STATUS";
- default:
- return "<Unknown>";
+ });
+ if (!success) {
+ mWakeLock.release();
}
}
@@ -1476,7 +1410,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void onReportLocation(boolean hasLatLong, Location location) {
- sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
+ postWithWakeLockHeld(() -> handleReportLocation(hasLatLong, location));
}
@Override
@@ -1502,7 +1436,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void onReportSvStatus(GnssStatus gnssStatus) {
- sendMessage(REPORT_SV_STATUS, 0, gnssStatus);
+ postWithWakeLockHeld(() -> handleReportSvStatus(gnssStatus));
}
@Override
@@ -1512,7 +1446,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void onRequestPsdsDownload(int psdsType) {
- sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+ postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
}
@Override
@@ -1558,7 +1492,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
+ ", isUserEmergency: "
+ isUserEmergency);
}
- sendMessage(REQUEST_LOCATION, independentFromGnss ? 1 : 0, isUserEmergency);
+ postWithWakeLockHeld(() -> handleRequestLocation(independentFromGnss, isUserEmergency));
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 21946ca88401..5de9cf3f5b6a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -331,7 +331,7 @@ public class GnssManagerService {
@Override
public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
- long ident = Binder.clearCallingIdentity();
+ final long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(LocationManager.ACTION_GNSS_CAPABILITIES_CHANGED)
.putExtra(LocationManager.EXTRA_GNSS_CAPABILITIES, newCapabilities)
diff --git a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
index 9874ecfdefdf..7369eac48c05 100644
--- a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
@@ -41,7 +41,7 @@ public class SystemDeviceStationaryHelper extends DeviceStationaryHelper {
public void addListener(DeviceIdleInternal.StationaryListener listener) {
Preconditions.checkState(mDeviceIdle != null);
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mDeviceIdle.registerStationaryListener(listener);
} finally {
@@ -53,7 +53,7 @@ public class SystemDeviceStationaryHelper extends DeviceStationaryHelper {
public void removeListener(DeviceIdleInternal.StationaryListener listener) {
Preconditions.checkState(mDeviceIdle != null);
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mDeviceIdle.unregisterStationaryListener(listener);
} finally {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0bec09cd003f..2efb239c81ed 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1620,7 +1620,7 @@ public class LockSettingsService extends ILockSettings.Stub {
+ PERMISSION);
}
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
enforceFrpResolved();
// When changing credential for profiles with unified challenge, some callers
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 42b7c9d388a0..8a3329913142 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -354,6 +354,12 @@ public class NotificationHistoryDatabase {
}
}
+ public void unregisterFileCleanupReceiver() {
+ if(mContext != null) {
+ mContext.unregisterReceiver(mFileCleanupReceiver);
+ }
+ }
+
private static long safeParseLong(String fileName) {
// AtomicFile will create copies of the numeric files with ".new" and ".bak"
// over the course of its processing. If these files still exist on boot we need to clean
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 6da898acdafe..0aacd130c837 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -288,6 +288,7 @@ public class NotificationHistoryManager {
private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
userHistory.disableHistory();
+ userHistory.unregisterFileCleanupReceiver();
mUserPendingHistoryDisables.put(userId, false);
mHistoryEnabled.put(userId, false);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b54e8f973bd6..74ef4ca839de 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2320,14 +2320,16 @@ public class NotificationManagerService extends SystemService {
mUserProfiles.updateCache(getContext());
- telephonyManager.listen(new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (mCallState == state) return;
- if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
- mCallState = state;
- }
- }, PhoneStateListener.LISTEN_CALL_STATE);
+ if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ telephonyManager.listen(new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mCallState == state) return;
+ if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
+ mCallState = state;
+ }
+ }, PhoneStateListener.LISTEN_CALL_STATE);
+ }
mSettingsObserver = new SettingsObserver(mHandler);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f66cfa99ce98..7a00b86cb1a3 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -585,7 +585,8 @@ public final class NotificationRecord {
pw.println("null");
} else {
pw.print(val.getClass().getSimpleName());
- if (redact && (val instanceof CharSequence || val instanceof String)) {
+ if (redact && (val instanceof CharSequence) && shouldRedactStringExtra(key)) {
+ pw.print(String.format(" [length=%d]", ((CharSequence) val).length()));
// redact contents from bugreports
} else if (val instanceof Bitmap) {
pw.print(String.format(" (%dx%d)",
@@ -611,6 +612,19 @@ public final class NotificationRecord {
}
}
+ private boolean shouldRedactStringExtra(String key) {
+ if (key == null) return true;
+ switch (key) {
+ // none of these keys contain user-related information; they do not need to be redacted
+ case Notification.EXTRA_SUBSTITUTE_APP_NAME:
+ case Notification.EXTRA_TEMPLATE:
+ case "android.support.v4.app.extra.COMPAT_TEMPLATE":
+ return false;
+ default:
+ return true;
+ }
+ }
+
@Override
public final String toString() {
return String.format(
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0e2ff7523c85..3ca6a0d1fe10 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -429,4 +429,14 @@ public interface NotificationRecordLogger {
return NotificationChannelLogger.getLoggingImportance(channel, importance);
}
+ /**
+ * @param r NotificationRecord
+ * @return Whether the notification is a foreground service notification.
+ */
+ static boolean isForegroundService(@NonNull NotificationRecord r) {
+ if (r.getSbn() == null || r.getSbn().getNotification() == null) {
+ return false;
+ }
+ return (r.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 1a99fb0e55f3..249910dc09fa 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -63,7 +63,12 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
/* android.stats.sysui.NotificationImportance importance_asst = 19 */
r.getAssistantImportance(),
/* int32 assistant_hash = 20 */ p.getAssistantHash(),
- /* float assistant_ranking_score = 21 */ r.getRankingScore()
+ /* float assistant_ranking_score = 21 */ r.getRankingScore(),
+ /* bool is_ongoing = 22 */ r.getSbn().isOngoing(),
+ /* bool is_foreground_service = 23 */
+ NotificationRecordLogger.isForegroundService(r),
+ /* optional int64 timeout_millis = 24 */
+ r.getSbn().getNotification().getTimeoutAfter()
);
}
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index f47aa487744a..0a69aec76306 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -39,6 +39,9 @@ public final class VibratorHelper {
private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
+ private static final int CHIRP_LEVEL_DURATION_MILLIS = 100;
+ private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100;
+ private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50;
private final Vibrator mVibrator;
private final long[] mDefaultPattern;
@@ -102,6 +105,9 @@ public final class VibratorHelper {
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createFallbackVibration(boolean insistent) {
+ if (mVibrator.hasFrequencyControl()) {
+ return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent);
+ }
return createWaveformVibration(mFallbackPattern, insistent);
}
@@ -111,9 +117,32 @@ public final class VibratorHelper {
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createDefaultVibration(boolean insistent) {
+ if (mVibrator.hasFrequencyControl()) {
+ return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent);
+ }
return createWaveformVibration(mDefaultPattern, insistent);
}
+ private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
+ VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
+ .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
+ .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
+ .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
+ .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
+
+ if (insistent) {
+ return waveformBuilder
+ .addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS)
+ .build(/* repeat= */ 0);
+ }
+
+ VibrationEffect singleBeat = waveformBuilder.build();
+ return VibrationEffect.startComposition()
+ .addEffect(singleBeat)
+ .addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS)
+ .compose();
+ }
+
private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
int[] ar = resources.getIntArray(resId);
if (ar == null) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 53bf7b8aa8ae..c0a378a64e10 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,7 +32,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Binder;
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 28511070fc8e..e0f106536a4b 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -37,6 +37,7 @@ import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
import android.os.RemoteException;
@@ -461,8 +462,8 @@ public class ApkChecksums {
}
// Obtaining array of certificates used for signing the installer package.
- certs = installer.getSigningDetails().signatures;
- pastCerts = installer.getSigningDetails().pastSigningCertificates;
+ certs = installer.getSigningDetails().getSignatures();
+ pastCerts = installer.getSigningDetails().getPastSigningCertificates();
}
if (certs == null || certs.length == 0 || certs[0] == null) {
Slog.e(TAG, "Can't obtain certificates.");
@@ -664,8 +665,7 @@ public class ApkChecksums {
Map<Integer, byte[]> contentDigests = null;
try {
contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
- false).contentDigests;
+ SignatureSchemeVersion.SIGNING_BLOCK_V2, /* verifyFull */ false).contentDigests;
} catch (PackageParser.PackageParserException e) {
if (!(e.getCause() instanceof SignatureNotFoundException)) {
Slog.e(TAG, "Signature verification error", e);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 06ff69176bb7..56b77b594443 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -28,7 +28,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -147,7 +147,7 @@ public class AppsFilter implements Watchable, Snappable {
private final OverlayReferenceMapper mOverlayReferenceMapper;
private final StateProvider mStateProvider;
- private PackageParser.SigningDetails mSystemSigningDetails;
+ private SigningDetails mSystemSigningDetails;
private Set<String> mProtectedBroadcasts = new ArraySet<>();
private final Object mCacheLock = new Object();
@@ -951,7 +951,7 @@ public class AppsFilter implements Watchable, Snappable {
}
}
- private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
+ private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
PackageSetting pkgSetting) {
return pkgSetting.isSystem()
&& pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
@@ -1287,20 +1287,6 @@ public class AppsFilter implements Watchable, Snappable {
}
}
- // This package isn't technically installed and won't be written to settings, so we can
- // treat it as filtered until it's available again.
- final AndroidPackage targetPkg = targetPkgSetting.pkg;
- if (targetPkg == null) {
- if (DEBUG_LOGGING) {
- Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
- }
- return true;
- }
- if (targetPkg.isStaticSharedLibrary()) {
- // not an app, this filtering takes place at a higher level
- return false;
- }
- final String targetName = targetPkg.getPackageName();
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
}
@@ -1343,6 +1329,21 @@ public class AppsFilter implements Watchable, Snappable {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
+
+ // This package isn't technically installed and won't be written to settings, so we can
+ // treat it as filtered until it's available again.
+ final AndroidPackage targetPkg = targetPkgSetting.pkg;
+ if (targetPkg == null) {
+ if (DEBUG_LOGGING) {
+ Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
+ }
+ return true;
+ }
+ if (targetPkg.isStaticSharedLibrary()) {
+ // not an app, this filtering takes place at a higher level
+ return false;
+ }
+
try {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
@@ -1415,6 +1416,7 @@ public class AppsFilter implements Watchable, Snappable {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
}
+ final String targetName = targetPkg.getPackageName();
if (callingSharedPkgSettings != null) {
int size = callingSharedPkgSettings.size();
for (int index = 0; index < size; index++) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3019439a430b..fb68b23daedd 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -178,6 +178,16 @@ public class DefaultCrossProfileIntentFiltersUtils {
.addDataType("*/*")
.build();
+ /** Pick images can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ 0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
/** Open document intent can be forwarded to parent user. */
private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
new DefaultCrossProfileIntentFilter.Builder(
@@ -289,6 +299,7 @@ public class DefaultCrossProfileIntentFiltersUtils {
RECOGNIZE_SPEECH,
ACTION_PICK_RAW,
ACTION_PICK_DATA,
+ ACTION_PICK_IMAGES,
OPEN_DOCUMENT,
GET_CONTENT,
USB_DEVICE_ATTACHED,
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index bf323e7d4ff2..affacbbb0bc3 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -23,8 +23,8 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -412,14 +412,14 @@ class InstantAppRegistry implements Watchable, Snappable {
// into account but also allow the value from the old computation to avoid
// data loss.
if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
+ SigningDetails.CertCapabilities.INSTALLED_DATA)) {
return;
}
// For backwards compatibility we accept match based on any signature, since we may have
// recorded only the first for multiply-signed packages
- final String[] signaturesSha256Digests =
- PackageUtils.computeSignaturesSha256Digests(pkg.getSigningDetails().signatures);
+ final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
+ pkg.getSigningDetails().getSignatures());
for (String s : signaturesSha256Digests) {
if (s.equals(currentCookieSha256)) {
return;
@@ -1305,8 +1305,8 @@ class InstantAppRegistry implements Watchable, Snappable {
// We prefer the modern computation procedure where all certs are taken
// into account and delete the file derived via the legacy hash computation.
File newCookieFile = computeInstantCookieFile(pkg.getPackageName(),
- PackageUtils.computeSignaturesSha256Digest(pkg.getSigningDetails().signatures),
- userId);
+ PackageUtils.computeSignaturesSha256Digest(
+ pkg.getSigningDetails().getSignatures()), userId);
if (!pkg.getSigningDetails().hasSignatures()) {
Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 34caaf52b215..fe748896aad8 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -193,7 +193,7 @@ public class KeySetManagerService {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Passed invalid package to keyset validation.");
}
- ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().publicKeys;
+ ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().getPublicKeys();
if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package has invalid signing-key-set.");
@@ -226,7 +226,7 @@ public class KeySetManagerService {
PackageSetting ps = mPackages.get(pkg.getPackageName());
Objects.requireNonNull(ps, "pkg: " + pkg.getPackageName()
+ "does not have a corresponding entry in mPackages.");
- addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
+ addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().getPublicKeys());
if (pkg.getKeySetMapping() != null) {
addDefinedKeySetsToPackageLPw(ps, pkg.getKeySetMapping());
if (pkg.getUpgradeKeySets() != null) {
@@ -371,7 +371,7 @@ public class KeySetManagerService {
for (int i = 0; i < upgradeKeySets.length; i++) {
Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
if (upgradeSet != null
- && pkg.getSigningDetails().publicKeys.containsAll(upgradeSet)) {
+ && pkg.getSigningDetails().getPublicKeys().containsAll(upgradeSet)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5b2c80903ce5..d88e7394e853 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -407,11 +407,16 @@ public class LauncherAppsService extends SystemService {
@Override
public boolean shouldHideFromSuggestions(String packageName, UserHandle user) {
- if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
+ final int userId = user.getIdentifier();
+ if (!canAccessProfile(userId, "cannot get shouldHideFromSuggestions")) {
return false;
}
- final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(packageName,
- user.getIdentifier());
+ if (mPackageManagerInternal.filterAppAccess(
+ packageName, Binder.getCallingUid(), userId)) {
+ return false;
+ }
+ final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(
+ packageName, userId);
return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d7b244980cfc..fe419a613b01 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -254,7 +254,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mSessionsDir.mkdirs();
mApexManager = ApexManager.getInstance();
- mStagingManager = new StagingManager(context, apexParserSupplier);
+ mStagingManager = new StagingManager(context, apexParserSupplier,
+ mInstallThread.getLooper());
LocalServices.getService(SystemServiceManager.class).startService(
new Lifecycle(context, this));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00ef97d7a17a..e605652aeb4c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -46,9 +46,11 @@ import static com.android.internal.util.XmlUtils.writeUriAttribute;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
+import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -82,8 +84,8 @@ import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.SigningDetails;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -263,6 +265,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
+ /**
+ * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
+ * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#Q} before getting the
+ * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
+ * result will not trigger any user action in
+ * {@link #checkUserActionRequirement(PackageInstallerSession)}.
+ */
+ private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
+
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -366,7 +377,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private long mVersionCode;
@GuardedBy("mLock")
- private PackageParser.SigningDetails mSigningDetails;
+ private SigningDetails mSigningDetails;
@GuardedBy("mLock")
private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
@GuardedBy("mLock")
@@ -705,28 +716,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
- * Notified by the staging manager that pre-reboot verification is about to start. The
- * return value should be checked to decide whether it is OK to start pre-reboot
- * verification. In the case of a destroyed session, {@code false} is returned and there is
- * no need to start pre-reboot verification.
+ * Called when pre-reboot verification is about to start. This shouldn't be called
+ * on a destroyed session.
*/
- @Override
- public boolean notifyStartPreRebootVerification() {
+ private void notifyStartPreRebootVerification() {
synchronized (mLock) {
+ Preconditions.checkState(!mDestroyed);
if (mInPreRebootVerification) {
throw new IllegalStateException("Pre-reboot verification has started");
}
- if (mDestroyed) {
- return false;
- }
mInPreRebootVerification = true;
- return true;
}
}
/**
- * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
- * clean up the session if {@link #abandon()} has been called previously.
+ * Notified by the staging manager or PIS that pre-reboot verification has ended.
+ * Now it is safe to clean up the session if {@link #abandon()} has been called previously.
*/
@Override
public void notifyEndPreRebootVerification() {
@@ -750,6 +755,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootOrSystem();
Preconditions.checkArgument(isCommitted());
Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
+ notifyStartPreRebootVerification();
verify();
}
@@ -803,6 +809,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private PackageLite mPackageLite;
+ /**
+ * Keep the target sdk of a validated apk.
+ */
+ @GuardedBy("mLock")
+ private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
+
private static final FileFilter sAddedApkFilter = new FileFilter() {
@Override
public boolean accept(File file) {
@@ -1736,6 +1748,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
+ @WorkerThread
private void handleStreamValidateAndCommit() {
PackageManagerException unrecoverableFailure = null;
// This will track whether the session and any children were validated and are ready to
@@ -1985,6 +1998,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* exception is thrown.
* @throws PackageManagerException on an unrecoverable error.
*/
+ @WorkerThread
private boolean streamValidateAndCommit() throws PackageManagerException {
// TODO(patb): since the work done here for a parent session in a multi-package install is
// mostly superficial, consider splitting this method for the parent and
@@ -2102,9 +2116,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (isStaged()) {
mStagedSession.setSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyVerificationComplete(mStagedSession);
+ mStagedSession.notifyEndPreRebootVerification();
} else {
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, msg, null);
@@ -2148,6 +2160,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* immutable by the caller during the method call. Used to resolve child
* sessions Ids to actual object reference.
*/
+ @AnyThread
void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
synchronized (mLock) {
// Resolve null values to actual object references
@@ -2233,6 +2246,63 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ @WorkerThread
+ private static boolean checkUserActionRequirement(PackageInstallerSession session) {
+ if (session.isMultiPackage()) {
+ return false;
+ }
+
+ @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED;
+ // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
+ userActionRequirement = session.computeUserActionRequirement();
+ if (userActionRequirement == USER_ACTION_REQUIRED) {
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+
+ if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
+ final int validatedTargetSdk;
+ synchronized (session.mLock) {
+ validatedTargetSdk = session.mValidatedTargetSdk;
+ }
+
+ if (validatedTargetSdk != INVALID_TARGET_SDK_VERSION
+ && validatedTargetSdk < Build.VERSION_CODES.Q) {
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+
+ if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
+ if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed(
+ session.getInstallerPackageName(), session.getPackageName())) {
+ // Fall back to the non-silent update if a repeated installation is invoked
+ // within the throttle time.
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+ session.mSilentUpdatePolicy.track(session.getInstallerPackageName(),
+ session.getPackageName());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Find out any session needs user action.
+ *
+ * @return true if the session set requires user action for the installation, otherwise false.
+ */
+ @WorkerThread
+ private boolean sendPendingUserActionIntentIfNeeded() {
+ synchronized (mLock) {
+ assertNotChildLocked("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
+ }
+
+ return sessionContains(PackageInstallerSession::checkUserActionRequirement);
+ }
+
+ @WorkerThread
private void handleInstall() {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
@@ -2241,6 +2311,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
.write();
}
+ /**
+ * Stops the installation of the whole session set if one session needs user action
+ * in its belong session set. When the user answers the yes,
+ * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
+ * handled to come back here to check again.
+ */
+ if (sendPendingUserActionIntentIfNeeded()) {
+ return;
+ }
+
+
// Check if APEX update is allowed. We do this check in handleInstall, since this is one of
// the places that:
// * Shared between staged and non-staged APEX update flows.
@@ -2261,14 +2342,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
if (params.isStaged) {
- mStagingManager.commitSession(mStagedSession);
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
// though ideally, we just need to send session committed broadcast.
dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
- return;
- }
- verify();
+ mStagedSession.verifySession();
+ } else {
+ verify();
+ }
}
private void verify() {
@@ -2280,18 +2361,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private IntentSender getRemoteStatusReceiver() {
+ synchronized (mLock) {
+ return mRemoteStatusReceiver;
+ }
+ }
+
private void verifyNonStaged()
throws PackageManagerException {
final PackageManagerService.VerificationParams verifyingSession =
prepareForVerification();
- if (verifyingSession == null) {
- return;
- }
if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- childSessions = getChildSessionsLocked();
- }
+ final List<PackageInstallerSession> childSessions = getChildSessions();
// Spot check to reject a non-staged multi package install of APEXes and APKs.
if (!params.isStaged && containsApkSession()
&& sessionContains(s -> s.isApexSession())) {
@@ -2308,20 +2389,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
final PackageManagerService.VerificationParams verifyingChildSession =
session.prepareForVerification();
- if (verifyingChildSession != null) {
- verifyingChildSessions.add(verifyingChildSession);
- }
+ verifyingChildSessions.add(verifyingChildSession);
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
@@ -2349,10 +2424,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Session should contain at least one apk session for installation");
}
if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- childSessions = getChildSessionsLocked();
- }
+ final List<PackageInstallerSession> childSessions = getChildSessions();
List<PackageManagerService.InstallParams> installingChildSessions =
new ArrayList<>(childSessions.size());
boolean success = true;
@@ -2371,11 +2443,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
if (!success) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
@@ -2391,23 +2459,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* {@link PackageManagerService.VerificationParams} representing this new staged state or null
* in case permissions need to be requested before verification can proceed.
*/
- @Nullable
+ @NonNull
private PackageManagerService.VerificationParams prepareForVerification()
throws PackageManagerException {
assertNotLocked("makeSessionActive");
- @UserActionRequirement
- int userActionRequirement = USER_ACTION_NOT_NEEDED;
- // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
- if (!params.isMultiPackage) {
- userActionRequirement = computeUserActionRequirement();
- if (userActionRequirement == USER_ACTION_REQUIRED) {
- sendPendingUserActionIntent();
- return null;
- } // else, we'll wait until we parse to determine if we need to
- }
-
- boolean silentUpdatePolicyEnforceable = false;
synchronized (mLock) {
if (mRelinquished) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -2424,36 +2480,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
PackageLite result = parseApkLite();
if (result != null) {
mPackageLite = result;
- synchronized (mProgressLock) {
- mInternalProgress = 0.5f;
- computeProgressLocked(true);
- }
-
- extractNativeLibraries(
- mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
-
- if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
- if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
- sendPendingUserActionIntent();
- return null;
- }
- if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
- silentUpdatePolicyEnforceable = true;
+ if (!isApexSession()) {
+ synchronized (mProgressLock) {
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
}
+
+ extractNativeLibraries(
+ mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
}
}
- }
- if (silentUpdatePolicyEnforceable) {
- if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
- getInstallerPackageName(), getPackageName())) {
- // Fall back to the non-silent update if a repeated installation is invoked within
- // the throttle time.
- sendPendingUserActionIntent();
- return null;
- }
- mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
- }
- synchronized (mLock) {
return makeVerificationParamsLocked();
}
}
@@ -2466,11 +2502,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+ sendOnUserActionRequired(mContext, getRemoteStatusReceiver(), sessionId, intent);
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
@@ -2480,73 +2512,77 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* Prepares staged directory with any inherited APKs and returns the parsed package.
*/
+ @GuardedBy("mLock")
@Nullable
private PackageLite parseApkLite() throws PackageManagerException {
- // TODO(b/136257624): Some logic in this if block probably belongs in
- // makeInstallParams().
- if (!isMultiPackage() && !isApexSession()) {
- Objects.requireNonNull(mPackageName);
- Objects.requireNonNull(mSigningDetails);
- Objects.requireNonNull(mResolvedBaseFile);
+ if (isMultiPackage()) {
+ return null;
+ }
+ Objects.requireNonNull(mPackageName);
+ Objects.requireNonNull(mSigningDetails);
+ Objects.requireNonNull(mResolvedBaseFile);
- // If we haven't already parsed, inherit any packages and native libraries from existing
- // install that haven't been overridden.
- if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- try {
- final List<File> fromFiles = mResolvedInheritedFiles;
- final File toDir = stageDir;
+ // Inherit any packages and native libraries from existing install that
+ // haven't been overridden.
+ if (!isApexSession() && params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ try {
+ final List<File> fromFiles = mResolvedInheritedFiles;
+ final File toDir = stageDir;
- if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
- if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
- throw new IllegalStateException("mInheritedFilesBase == null");
- }
+ if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+ if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+ throw new IllegalStateException("mInheritedFilesBase == null");
+ }
- if (isLinkPossible(fromFiles, toDir)) {
- if (!mResolvedInstructionSets.isEmpty()) {
- final File oatDir = new File(toDir, "oat");
- createOatDirs(mResolvedInstructionSets, oatDir);
- }
- // pre-create lib dirs for linking if necessary
- if (!mResolvedNativeLibPaths.isEmpty()) {
- for (String libPath : mResolvedNativeLibPaths) {
- // "/lib/arm64" -> ["lib", "arm64"]
- final int splitIndex = libPath.lastIndexOf('/');
- if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
- Slog.e(TAG,
- "Skipping native library creation for linking due"
- + " to invalid path: " + libPath);
- continue;
- }
- final String libDirPath = libPath.substring(1, splitIndex);
- final File libDir = new File(toDir, libDirPath);
- if (!libDir.exists()) {
- NativeLibraryHelper.createNativeLibrarySubdir(libDir);
- }
- final String archDirPath = libPath.substring(splitIndex + 1);
- NativeLibraryHelper.createNativeLibrarySubdir(
- new File(libDir, archDirPath));
+ if (isLinkPossible(fromFiles, toDir)) {
+ if (!mResolvedInstructionSets.isEmpty()) {
+ final File oatDir = new File(toDir, "oat");
+ createOatDirs(mResolvedInstructionSets, oatDir);
+ }
+ // pre-create lib dirs for linking if necessary
+ if (!mResolvedNativeLibPaths.isEmpty()) {
+ for (String libPath : mResolvedNativeLibPaths) {
+ // "/lib/arm64" -> ["lib", "arm64"]
+ final int splitIndex = libPath.lastIndexOf('/');
+ if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+ Slog.e(TAG,
+ "Skipping native library creation for linking due"
+ + " to invalid path: " + libPath);
+ continue;
+ }
+ final String libDirPath = libPath.substring(1, splitIndex);
+ final File libDir = new File(toDir, libDirPath);
+ if (!libDir.exists()) {
+ NativeLibraryHelper.createNativeLibrarySubdir(libDir);
}
+ final String archDirPath = libPath.substring(splitIndex + 1);
+ NativeLibraryHelper.createNativeLibrarySubdir(
+ new File(libDir, archDirPath));
}
- linkFiles(fromFiles, toDir, mInheritedFilesBase);
- } else {
- // TODO: this should delegate to DCS so the system process
- // avoids holding open FDs into containers.
- copyFiles(fromFiles, toDir);
}
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Failed to inherit existing install", e);
+ linkFiles(fromFiles, toDir, mInheritedFilesBase);
+ } else {
+ // TODO: this should delegate to DCS so the system process
+ // avoids holding open FDs into containers.
+ copyFiles(fromFiles, toDir);
}
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to inherit existing install", e);
}
+ }
+
+ if (!isApexSession()) {
// For mode inherit existing, it would link/copy existing files to stage dir in the
// above block. Therefore, we need to parse the complete package in stage dir here.
- // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
- // verification.
+ // Besides, PackageLite may be null for staged sessions that don't complete
+ // pre-reboot verification.
return getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+ } else {
+ return getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
}
- return null;
}
@GuardedBy("mLock")
@@ -2587,25 +2623,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mRelinquished = true;
- // TODO(b/169375643): Remove this workaround once b/161121612 is fixed.
- PackageInstaller.SessionParams copiedParams = params.copy();
- if (params.isStaged) {
- // This is called by the pre-reboot verification. Don't enable rollback here since
- // it has been enabled when pre-reboot verification starts.
- copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
- }
- return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
+ return mPm.new VerificationParams(user, stageDir, localObserver, params,
mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite);
}
private void onVerificationComplete() {
- // Staged sessions will be installed later during boot
+ // APK verification is done. Continue the installation depending on whether it is a
+ // staged session or not. For a staged session, we will hand it over to the staging
+ // manager to complete the installation.
if (isStaged()) {
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
- // TODO(b/136257624): We also need to destroy internals for verified staged session,
- // otherwise file descriptors are never closed for verified staged session until reboot
+ mStagingManager.commitSession(mStagedSession);
return;
}
@@ -2855,6 +2882,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
+
+ mSigningDetails = apk.getSigningDetails();
}
/**
@@ -2873,11 +2902,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private PackageLite validateApkInstallLocked() throws PackageManagerException {
ApkLite baseApk = null;
- PackageLite packageLite = null;
+ final PackageLite packageLite;
mPackageLite = null;
mPackageName = null;
mVersionCode = -1;
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
mResolvedBaseFile = null;
mResolvedStagedFiles.clear();
@@ -2940,7 +2969,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = apk.getSigningDetails();
}
@@ -2995,7 +3024,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = pkgInfo.packageName;
mVersionCode = pkgInfo.getLongVersionCode();
}
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = unsafeGetCertsWithoutVerification(
pkgInfo.applicationInfo.sourceDir);
}
@@ -3055,7 +3084,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
packageLite = existing;
assertPackageConsistentLocked("Existing", existing.getPackageName(),
existing.getLongVersionCode());
- final PackageParser.SigningDetails signingDetails =
+ final SigningDetails signingDetails =
unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -3180,6 +3209,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mIncrementalFileStorages.disallowReadLogs();
}
}
+
+ // {@link #sendPendingUserActionIntentIfNeeded} needs to use
+ // {@link PackageLite#getTargetSdk()}
+ mValidatedTargetSdk = packageLite.getTargetSdk();
+
return packageLite;
}
@@ -3395,11 +3429,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private PackageParser.SigningDetails unsafeGetCertsWithoutVerification(String path)
+ private SigningDetails unsafeGetCertsWithoutVerification(String path)
throws PackageManagerException {
try {
return ApkSignatureVerifier.unsafeGetCertsWithoutVerification(path,
- PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
+ SigningDetails.SignatureSchemeVersion.JAR);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Couldn't obtain signatures from APK : " + path);
@@ -3591,8 +3625,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Mark and kick off another install pass
synchronized (mLock) {
mPermissionsManuallyAccepted = true;
- mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
+
+ PackageInstallerSession root =
+ (hasParentSessionId())
+ ? mSessionProvider.getSession(getParentSessionId())
+ : this;
+ root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
} else {
destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -3878,11 +3917,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendPendingStreaming(mContext, statusReceiver, sessionId,
+ sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
"DataLoader unavailable");
break;
}
@@ -3896,11 +3931,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
+ sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
+ e.getMessage());
}
}
};
@@ -4169,18 +4201,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
- final IntentSender statusReceiver;
- final String packageName;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- packageName = mPackageName;
- }
+ final IntentSender statusReceiver = getRemoteStatusReceiver();
if (statusReceiver != null) {
// Execute observer.onPackageInstalled on different thread as we don't want callers
// inside the system server have to worry about catching the callbacks while they are
// calling into the session
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
+ args.arg1 = getPackageName();
args.arg2 = msg;
args.arg3 = extras;
args.arg4 = statusReceiver;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee44c10edbf3..be06ec08eb55 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -95,7 +95,7 @@ import static android.content.pm.PackageManager.TYPE_SERVICE;
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED;
@@ -147,6 +147,7 @@ import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
+import android.app.PendingIntent;
import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
@@ -159,6 +160,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -210,8 +212,6 @@ import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackagePartitions;
import android.content.pm.PackagePartitions.SystemPartition;
import android.content.pm.PackageStats;
@@ -226,6 +226,8 @@ import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.SigningInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.TestUtilityService;
@@ -1354,7 +1356,6 @@ public class PackageManagerService extends IPackageManager.Stub
public DefaultAppProvider defaultAppProvider;
public DexManager dexManager;
public List<ScanPartition> dirsToScanAsSystem;
- public @Nullable String documenterPackage;
public boolean factoryTest;
public ArrayMap<String, FeatureInfo> availableFeatures;
public Handler handler;
@@ -1703,7 +1704,6 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mStorageManagerPackage;
final @Nullable String mDefaultTextClassifierPackage;
final @Nullable String mSystemTextClassifierPackageName;
- final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
@@ -6989,7 +6989,6 @@ public class PackageManagerService extends IPackageManager.Stub
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mRecentsPackage = testParams.recentsPackage;
- mDocumenterPackage = testParams.documenterPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -7588,7 +7587,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
- mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
@@ -9781,7 +9779,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (p2SigningDetails == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
- int result = compareSignatures(p1SigningDetails.signatures, p2SigningDetails.signatures);
+ int result = compareSignatures(p1SigningDetails.getSignatures(),
+ p2SigningDetails.getSignatures());
if (result == PackageManager.SIGNATURE_MATCH) {
return result;
}
@@ -9791,11 +9790,11 @@ public class PackageManagerService extends IPackageManager.Stub
if (p1SigningDetails.hasPastSigningCertificates()
|| p2SigningDetails.hasPastSigningCertificates()) {
Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
- : p1SigningDetails.signatures;
+ ? new Signature[]{p1SigningDetails.getPastSigningCertificates()[0]}
+ : p1SigningDetails.getSignatures();
Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
- : p2SigningDetails.signatures;
+ ? new Signature[]{p2SigningDetails.getPastSigningCertificates()[0]}
+ : p2SigningDetails.getSignatures();
result = compareSignatures(p1Signatures, p2Signatures);
}
return result;
@@ -9839,7 +9838,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mLock) {
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
@@ -10133,7 +10132,22 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstantAppPackageName(getCallingUid()) != null) {
return EmptyArray.STRING;
}
- return mPermissionManager.getAppOpPermissionPackages(permissionName);
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ final ArraySet<String> packageNames = new ArraySet(
+ mPermissionManager.getAppOpPermissionPackages(permissionName));
+ synchronized (mLock) {
+ for (int i = packageNames.size() - 1; i >= 0; i--) {
+ final String packageName = packageNames.valueAt(i);
+ if (!shouldFilterApplicationLocked(mSettings.getPackageLPr(packageName),
+ callingUid, callingUserId)) {
+ continue;
+ }
+ packageNames.removeAt(i);
+ }
+ }
+ return packageNames.toArray(new String[packageNames.size()]);
}
@Override
@@ -11888,14 +11902,14 @@ public class PackageManagerService extends IPackageManager.Stub
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
- if (ps.signatures.mSigningDetails.signatures != null
- && ps.signatures.mSigningDetails.signatures.length != 0
- && ps.signatures.mSigningDetails.signatureSchemeVersion
+ if (ps.signatures.mSigningDetails.getSignatures() != null
+ && ps.signatures.mSigningDetails.getSignatures().length != 0
+ && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
!= SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
parsedPackage.setSigningDetails(
- new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
+ new SigningDetails(ps.signatures.mSigningDetails));
return;
}
@@ -12199,10 +12213,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (!parsedPackage.getSigningDetails()
.checkCapability(pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
@@ -13503,9 +13517,9 @@ public class PackageManagerService extends IPackageManager.Stub
// For apps targeting O MR1 we require explicit enumeration of all certs.
final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
? PackageUtils.computeSignaturesSha256Digests(
- libPkg.signatures)
+ libPkg.getSignatures())
: PackageUtils.computeSignaturesSha256Digests(
- new Signature[]{libPkg.signatures[0]});
+ new Signature[]{libPkg.getSignatures()[0]});
// Take a shortcut if sizes don't match. Note that if an app doesn't
// target O we don't parse the "additional-certificate" tags similarly
@@ -13846,8 +13860,9 @@ public class PackageManagerService extends IPackageManager.Stub
// priv-apps.
synchronized (mLock) {
PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
- pkg.getSigningDetails().signatures)
+ if ((compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+ pkg.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
@@ -14686,8 +14701,8 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage.setSignedWithPlatformKey(
(PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
|| (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().signatures,
- parsedPackage.getSigningDetails().signatures
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
) == PackageManager.SIGNATURE_MATCH))
);
@@ -14983,7 +14998,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Exempt SharedUsers signed with the platform key.
PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ 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 " +
@@ -15031,7 +15046,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting platformPkgSetting =
mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName()
+ " must target Q or later, "
@@ -15051,7 +15066,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.getPackageLPr(pkg.getOverlayTarget());
if (targetPkgSetting != null) {
if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
// check reference signature
if (mOverlayConfigSignaturePackage == null) {
throw new PackageManagerException("Overlay "
@@ -15063,7 +15078,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting refPkgSetting =
mSettings.getPackageLPr(mOverlayConfigSignaturePackage);
if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName() + " signed with a different "
+ "certificate than both the reference package and "
@@ -15082,7 +15097,8 @@ public class PackageManagerService extends IPackageManager.Stub
int minSignatureSchemeVersion =
ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
+ 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());
@@ -16892,7 +16908,7 @@ public class PackageManagerService extends IPackageManager.Stub
final AndroidPackage pkg = mPackages.get(verifierInfo.packageName);
if (pkg == null) {
return -1;
- } else if (pkg.getSigningDetails().signatures.length != 1) {
+ } else if (pkg.getSigningDetails().getSignatures().length != 1) {
Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ " has more than one signature; ignoring");
return -1;
@@ -16906,7 +16922,7 @@ public class PackageManagerService extends IPackageManager.Stub
final byte[] expectedPublicKey;
try {
- final Signature verifierSig = pkg.getSigningDetails().signatures[0];
+ final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0];
final PublicKey publicKey = verifierSig.getPublicKey();
expectedPublicKey = publicKey.getEncoded();
} catch (CertificateException e) {
@@ -17170,9 +17186,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
- ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
+ ((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
} else if (obj instanceof PackageSetting) {
- callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
+ callerSignature =
+ ((PackageSetting) obj).signatures.mSigningDetails.getSignatures();
} else {
throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
@@ -17184,7 +17201,7 @@ public class PackageManagerService extends IPackageManager.Stub
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
if (compareSignatures(callerSignature,
- installerPackageSetting.signatures.mSigningDetails.signatures)
+ installerPackageSetting.signatures.mSigningDetails.getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
@@ -17201,7 +17218,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
- targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
+ targetInstallerPkgSetting.signatures.mSigningDetails.getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
@@ -17770,7 +17787,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
final int autoRevokePermissionsMode;
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final int installReason;
final int mInstallScenario;
@Nullable MultiPackageInstallParams mParentInstallParams;
@@ -17794,7 +17811,7 @@ public class PackageManagerService extends IPackageManager.Stub
this.grantedRuntimePermissions = null;
this.whitelistedRestrictedPermissions = null;
this.autoRevokePermissionsMode = MODE_DEFAULT;
- this.signingDetails = PackageParser.SigningDetails.UNKNOWN;
+ this.signingDetails = SigningDetails.UNKNOWN;
this.installReason = PackageManager.INSTALL_REASON_UNKNOWN;
this.mInstallScenario = PackageManager.INSTALL_SCENARIO_DEFAULT;
this.forceQueryableOverride = false;
@@ -18089,7 +18106,7 @@ public class PackageManagerService extends IPackageManager.Stub
@NonNull final InstallSource installSource;
final String packageAbiOverride;
final VerificationInfo verificationInfo;
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
@Nullable MultiPackageVerificationParams mParentVerificationParams;
final long requiredInstalledVersionCode;
final int mDataLoaderType;
@@ -18132,13 +18149,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
public void handleStartCopy() {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- // Apex packages get verified in StagingManager currently.
- // TODO(b/136257624): Move apex verification logic out of StagingManager
- mRet = INSTALL_SUCCEEDED;
- return;
- }
-
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
@@ -18150,7 +18160,10 @@ public class PackageManagerService extends IPackageManager.Stub
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!origin.existing) {
- sendApkVerificationRequest(pkgLite);
+ if ((installFlags & PackageManager.INSTALL_APEX) == 0) {
+ // TODO(b/182426975): treat APEX as APK when APK verification is concerned
+ sendApkVerificationRequest(pkgLite);
+ }
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
sendEnableRollbackRequest();
}
@@ -18197,28 +18210,23 @@ public class PackageManagerService extends IPackageManager.Stub
// it will not miss the broadcast.
enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
- android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // the duration to wait for rollback to be enabled, in millis
- long rollbackTimeout = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
- if (rollbackTimeout < 0) {
- rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
- }
- final Message msg = mHandler.obtainMessage(
- ENABLE_ROLLBACK_TIMEOUT);
- msg.arg1 = enableRollbackToken;
- msg.arg2 = mSessionId;
- mHandler.sendMessageDelayed(msg, rollbackTimeout);
- }
- }, null, 0, null, null);
+ mContext.sendBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
+ android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
mWaitForEnableRollbackToComplete = true;
+
+ // the duration to wait for rollback to be enabled, in millis
+ long rollbackTimeout = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
+ DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
+ if (rollbackTimeout < 0) {
+ rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
+ }
+ final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_TIMEOUT);
+ msg.arg1 = enableRollbackToken;
+ msg.arg2 = mSessionId;
+ mHandler.sendMessageDelayed(msg, rollbackTimeout);
}
/**
@@ -18311,7 +18319,7 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean isVerificationEnabled = isVerificationEnabled(
pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
final boolean isV4Signed =
- (signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
+ (signingDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4);
final boolean isIncrementalInstall =
(mDataLoaderType == DataLoaderType.INCREMENTAL);
// NOTE: We purposefully skip verification for only incremental installs when there's
@@ -18542,7 +18550,7 @@ public class PackageManagerService extends IPackageManager.Stub
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final int installReason;
final int mInstallScenario;
final boolean forceQueryableOverride;
@@ -18680,7 +18688,7 @@ public class PackageManagerService extends IPackageManager.Stub
FileInstallArgs(String codePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
- PackageParser.SigningDetails.UNKNOWN,
+ SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
false, DataLoaderType.NONE);
this.codeFile = (codePath != null) ? new File(codePath) : null;
@@ -19531,11 +19539,11 @@ public class PackageManagerService extends IPackageManager.Stub
// 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.sharedUser != null) {
- final Signature[] sharedUserSignatures =
- signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
+ final Signature[] sharedUserSignatures = signatureCheckPs.sharedUser
+ .signatures.mSigningDetails.getSignatures();
if (signatureCheckPs.sharedUser.signaturesChanged != null
&& compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().signatures)
+ 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
@@ -19939,7 +19947,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
continue;
}
- if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
+ if (args.signingDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
continue;
}
// For incremental installs, we bypass the verifier prior to install. Now
@@ -20365,11 +20373,11 @@ public class PackageManagerService extends IPackageManager.Stub
// older certificate with which the current is ok with sharing permissions
if (sourceSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ SigningDetails.CertCapabilities.PERMISSION)) {
return true;
} else if (parsedPackage.getSigningDetails().checkCapability(
sourceSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ SigningDetails.CertCapabilities.PERMISSION)) {
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
synchronized (mLock) {
@@ -20484,7 +20492,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
// either use what we've been given or parse directly from the APK
- if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ if (args.signingDetails != SigningDetails.UNKNOWN) {
parsedPackage.setSigningDetails(args.signingDetails);
} else {
parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails(
@@ -20494,7 +20502,7 @@ public class PackageManagerService extends IPackageManager.Stub
throw new PrepareFailure("Failed collect during installPackageLI", e);
}
- if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
+ if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion()
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " is not signed with at least APK Signature Scheme v2");
@@ -23581,25 +23589,6 @@ public class PackageManagerService extends IPackageManager.Stub
getPackageFromComponentString(R.string.config_defaultRotationResolverService));
}
- private @Nullable String getDocumenterPackageName() {
- final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("*/*");
- final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
-
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
- MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
- | MATCH_DISABLED_COMPONENTS,
- UserHandle.myUserId());
- if (matches.size() == 1) {
- return matches.get(0).getComponentInfo().packageName;
- } else {
- Slog.e(TAG, "There should probably be exactly one documenter; found "
- + matches.size() + ": matches=" + matches);
- return null;
- }
- }
-
@Nullable
private String getDeviceConfiguratorPackageName() {
return ensureSystemPackageName(mContext.getString(
@@ -23667,10 +23656,10 @@ public class PackageManagerService extends IPackageManager.Stub
final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
if (androidPkg != null) {
final SigningDetails signingDetail = androidPkg.getSigningDetails();
- if (signingDetail != null && signingDetail.signatures != null) {
+ if (signingDetail != null && signingDetail.getSignatures() != null) {
try {
final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
- for (Signature signature : signingDetail.signatures) {
+ for (Signature signature : signingDetail.getSignatures()) {
if (TextUtils.equals(predefinedSignature,
HexEncoding.encodeToString(msgDigest.digest(
signature.toByteArray()), false))) {
@@ -23860,7 +23849,6 @@ public class PackageManagerService extends IPackageManager.Stub
throw new IllegalArgumentException("Invalid new component state: "
+ newState);
}
- PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
final int permission;
if (callingUid == Process.SYSTEM_UID) {
@@ -23874,55 +23862,43 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
boolean isApp = (className == null);
- final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
String componentName = isApp ? packageName : className;
ArrayList<String> components;
+ final boolean isCallerTargetApp = ArrayUtils.contains(
+ getPackagesForUid(callingUid), packageName);
+ final PackageSetting pkgSetting;
// reader
synchronized (mLock) {
pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null) {
- if (!isCallerInstantApp) {
- if (className == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- throw new IllegalArgumentException(
- "Unknown component: " + packageName + "/" + className);
- } else {
- // throw SecurityException to prevent leaking package information
+ // Limit who can change which apps
+ if (!isCallerTargetApp) {
+ // Don't allow apps that don't have permission to modify other apps
+ if (!allowedByPermission
+ || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
throw new SecurityException(
"Attempt to change component state; "
- + "pid=" + Binder.getCallingPid()
- + ", uid=" + callingUid
- + (className == null
- ? ", package=" + packageName
- : ", component=" + packageName + "/" + className));
+ + "pid=" + Binder.getCallingPid()
+ + ", uid=" + callingUid
+ + (className == null
+ ? ", package=" + packageName
+ : ", component=" + packageName + "/" + className));
+ }
+ // Don't allow changing protected packages.
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ throw new SecurityException(
+ "Cannot disable a protected package: " + packageName);
}
}
- }
-
- // Limit who can change which apps
- if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
- // Don't allow apps that don't have permission to modify other apps
- final boolean filterApp;
- synchronized (mLock) {
- filterApp = (!allowedByPermission
- || shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
- }
- if (filterApp) {
- throw new SecurityException(
- "Attempt to change component state; "
- + "pid=" + Binder.getCallingPid()
- + ", uid=" + callingUid
- + (className == null
- ? ", package=" + packageName
- : ", component=" + packageName + "/" + className));
- }
- // Don't allow changing protected packages.
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- throw new SecurityException("Cannot disable a protected package: " + packageName);
+ if (pkgSetting == null) {
+ if (className == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ throw new IllegalArgumentException(
+ "Unknown component: " + packageName + "/" + className);
}
}
+
// Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
// app details activity
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)
@@ -26716,6 +26692,11 @@ public class PackageManagerService extends IPackageManager.Stub
private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
long requiredInstalledVersionCode, int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ return verifyReplacingVersionCodeForApex(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
String packageName = pkgLite.packageName;
synchronized (mLock) {
// Package which currently owns the data that the new package will own if installed.
@@ -26762,6 +26743,40 @@ public class PackageManagerService extends IPackageManager.Stub
return PackageManager.INSTALL_SUCCEEDED;
}
+ private int verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ String packageName = pkgLite.packageName;
+
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (activePackage == null) {
+ Slog.w(TAG, "Attempting to install new APEX package " + packageName);
+ return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ final long activeVersion = activePackage.getLongVersionCode();
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activeVersion != requiredInstalledVersionCode) {
+ Slog.w(TAG, "Installed version of APEX package " + packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + requiredInstalledVersionCode);
+ return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final long newVersionCode = pkgLite.getLongVersionCode();
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+ && newVersionCode < activeVersion) {
+ Slog.w(TAG, "Downgrade of APEX package " + packageName
+ + " is not allowed. Active version: " + activeVersion
+ + " attempted: " + newVersionCode);
+ return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+ }
+
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
/**
* Check and throw if the given before/after packages would be considered a
* downgrade.
@@ -27090,7 +27105,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
return pkg.getSigningDetails().hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
|| mPlatformPackage.getSigningDetails().checkCapability(pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ SigningDetails.CertCapabilities.PERMISSION);
}
@Override
@@ -27267,8 +27282,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
- case PackageManagerInternal.PACKAGE_DOCUMENTER:
- return filterOnlySystemPackages(mDocumenterPackage);
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
return filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
@@ -28952,6 +28965,56 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ @Override
+ public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId) throws RemoteException {
+ Objects.requireNonNull(packageName);
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get launch intent sender for package");
+ final int packageUid = getPackageUid(callingPackage, 0 /* flags */, userId);
+ if (!UserHandle.isSameApp(callingUid, packageUid)) {
+ throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
+ + callingUid + " does not own package: " + callingPackage);
+ }
+
+ // Using the same implementation with the #getLaunchIntentForPackage to get the ResolveInfo.
+ // Pass the resolveForStart as true in queryIntentActivities to skip the app filtering.
+ final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_INFO);
+ intentToResolve.setPackage(packageName);
+ String resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+ List<ResolveInfo> ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+ true /* resolveForStart */, false /* allowDynamicSplits */);
+ if (ris == null || ris.size() <= 0) {
+ intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.setPackage(packageName);
+ resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+ ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+ true /* resolveForStart */, false /* allowDynamicSplits */);
+ }
+
+ final Intent intent = new Intent(intentToResolve);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // For the case of empty result, no component name is assigned into the intent. A
+ // non-launchable IntentSender which contains the failed intent is created. The
+ // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
+ if (ris != null && !ris.isEmpty()) {
+ intent.setClassName(ris.get(0).activityInfo.packageName,
+ ris.get(0).activityInfo.name);
+ }
+ final IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ featureId, null /* token */, null /* resultWho */,
+ 1 /* requestCode */, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null,
+ PendingIntent.FLAG_IMMUTABLE, null /* bOptions */, userId);
+ return new IntentSender(target);
+ }
+
private static class TempUserState {
public final int enabledState;
@Nullable
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 8b5abf3afe51..5fdb57d5b873 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,9 +34,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
@@ -499,9 +499,9 @@ public class PackageManagerServiceUtils {
*/
public static boolean comparePackageSignatures(PackageSetting pkgSetting,
Signature[] signatures) {
- return pkgSetting.signatures.mSigningDetails
- == PackageParser.SigningDetails.UNKNOWN
- || compareSignatures(pkgSetting.signatures.mSigningDetails.signatures, signatures)
+ final SigningDetails signingDetails = pkgSetting.signatures.mSigningDetails;
+ return signingDetails == SigningDetails.UNKNOWN
+ || compareSignatures(signingDetails.getSignatures(), signatures)
== PackageManager.SIGNATURE_MATCH;
}
@@ -512,13 +512,13 @@ public class PackageManagerServiceUtils {
* system upgrade) and {@code scannedSigs} will be in the newer format.
*/
private static boolean matchSignaturesCompat(String packageName,
- PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
+ PackageSignatures packageSignatures, SigningDetails parsedSignatures) {
ArraySet<Signature> existingSet = new ArraySet<Signature>();
- for (Signature sig : packageSignatures.mSigningDetails.signatures) {
+ for (Signature sig : packageSignatures.mSigningDetails.getSignatures()) {
existingSet.add(sig);
}
ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
- for (Signature sig : parsedSignatures.signatures) {
+ for (Signature sig : parsedSignatures.getSignatures()) {
try {
Signature[] chainSignatures = sig.getChainSignatures();
for (Signature chainSig : chainSignatures) {
@@ -547,9 +547,9 @@ public class PackageManagerServiceUtils {
private static boolean matchSignaturesRecover(
String packageName,
- PackageParser.SigningDetails existingSignatures,
- PackageParser.SigningDetails parsedSignatures,
- @PackageParser.SigningDetails.CertCapabilities int flags) {
+ SigningDetails existingSignatures,
+ SigningDetails parsedSignatures,
+ @SigningDetails.CertCapabilities int flags) {
String msg = null;
try {
if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
@@ -576,10 +576,10 @@ public class PackageManagerServiceUtils {
PackageSetting disabledPkgSetting) {
if (pkgSetting.signatures.mSigningDetails.checkCapability(
disabledPkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| disabledPkgSetting.signatures.mSigningDetails.checkCapability(
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ SigningDetails.CertCapabilities.ROLLBACK)) {
return true;
} else {
logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
@@ -623,19 +623,19 @@ public class PackageManagerServiceUtils {
* @throws PackageManagerException if the signatures did not match.
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
- PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
+ PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
- if (pkgSetting.signatures.mSigningDetails.signatures != null) {
+ if (pkgSetting.signatures.mSigningDetails.getSignatures() != null) {
// Already existing package. Make sure signatures match
boolean match = parsedSignatures.checkCapability(
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| pkgSetting.signatures.mSigningDetails.checkCapability(
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+ SigningDetails.CertCapabilities.ROLLBACK);
if (!match && compareCompat) {
match = matchSignaturesCompat(packageName, pkgSetting.signatures,
parsedSignatures);
@@ -646,12 +646,12 @@ public class PackageManagerServiceUtils {
packageName,
pkgSetting.signatures.mSigningDetails,
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| matchSignaturesRecover(
packageName,
parsedSignatures,
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+ SigningDetails.CertCapabilities.ROLLBACK);
}
if (!match && isApkVerificationForced(disabledPkgSetting)) {
@@ -674,7 +674,7 @@ public class PackageManagerServiceUtils {
// Check for shared user signatures
if (pkgSetting.getSharedUser() != null
&& pkgSetting.getSharedUser().signatures.mSigningDetails
- != PackageParser.SigningDetails.UNKNOWN) {
+ != SigningDetails.UNKNOWN) {
// Already existing package. Make sure signatures match. In case of signing certificate
// rotation, the packages with newer certs need to be ok with being sharedUserId with
@@ -684,10 +684,10 @@ public class PackageManagerServiceUtils {
boolean match =
parsedSignatures.checkCapability(
pkgSetting.getSharedUser().signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+ SigningDetails.CertCapabilities.SHARED_USER_ID)
|| pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+ SigningDetails.CertCapabilities.SHARED_USER_ID);
// Special case: if the sharedUserId capability check failed it could be due to this
// being the only package in the sharedUserId so far and the lineage being updated to
// deny the sharedUserId capability of the previous key in the lineage.
@@ -704,11 +704,11 @@ public class PackageManagerServiceUtils {
matchSignaturesRecover(packageName,
pkgSetting.getSharedUser().signatures.mSigningDetails,
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+ SigningDetails.CertCapabilities.SHARED_USER_ID)
|| matchSignaturesRecover(packageName,
parsedSignatures,
pkgSetting.getSharedUser().signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+ SigningDetails.CertCapabilities.SHARED_USER_ID);
compatMatch |= match;
}
if (!match) {
@@ -729,13 +729,13 @@ public class PackageManagerServiceUtils {
if (packageName.equals(shUidPkgSetting.name)) {
continue;
}
- PackageParser.SigningDetails shUidSigningDetails =
+ SigningDetails shUidSigningDetails =
shUidPkgSetting.getSigningDetails();
// The capability check only needs to be performed against the package if it is
// signed with a key that is in the lineage of the package being installed.
if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
if (!parsedSignatures.checkCapability(shUidSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)) {
+ SigningDetails.CertCapabilities.SHARED_USER_ID)) {
throw new PackageManagerException(
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bf82bd8b09d5..3d1a841fd5e7 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -27,9 +27,9 @@ import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager.UninstallReason;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
@@ -226,10 +226,10 @@ public abstract class PackageSettingBase extends SettingBase {
}
public Signature[] getSignatures() {
- return signatures.mSigningDetails.signatures;
+ return signatures.mSigningDetails.getSignatures();
}
- public PackageParser.SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return signatures.mSigningDetails;
}
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 394cdee8f917..a1e505199a96 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -17,9 +17,9 @@
package com.android.server.pm;
import android.annotation.NonNull;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.util.Log;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -35,40 +35,41 @@ import java.util.ArrayList;
class PackageSignatures {
- @NonNull PackageParser.SigningDetails mSigningDetails;
+ @NonNull SigningDetails mSigningDetails;
PackageSignatures(PackageSignatures orig) {
- if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) {
- mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ if (orig != null && orig.mSigningDetails != SigningDetails.UNKNOWN) {
+ mSigningDetails = new SigningDetails(orig.mSigningDetails);
} else {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
}
- PackageSignatures(PackageParser.SigningDetails signingDetails) {
+ PackageSignatures(SigningDetails signingDetails) {
mSigningDetails = signingDetails;
}
PackageSignatures() {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
void writeXml(TypedXmlSerializer serializer, String tagName,
ArrayList<Signature> writtenSignatures) throws IOException {
- if (mSigningDetails.signatures == null) {
+ if (mSigningDetails.getSignatures() == null) {
return;
}
serializer.startTag(null, tagName);
- serializer.attributeInt(null, "count", mSigningDetails.signatures.length);
- serializer.attributeInt(null, "schemeVersion", mSigningDetails.signatureSchemeVersion);
- writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
+ serializer.attributeInt(null, "count", mSigningDetails.getSignatures().length);
+ serializer.attributeInt(null, "schemeVersion", mSigningDetails.getSignatureSchemeVersion());
+ writeCertsListXml(serializer, writtenSignatures, mSigningDetails.getSignatures(), false);
// if we have past signer certificate information, write it out
- if (mSigningDetails.pastSigningCertificates != null) {
+ if (mSigningDetails.getPastSigningCertificates() != null) {
serializer.startTag(null, "pastSigs");
- serializer.attributeInt(null, "count", mSigningDetails.pastSigningCertificates.length);
+ serializer.attributeInt(null, "count",
+ mSigningDetails.getPastSigningCertificates().length);
writeCertsListXml(serializer, writtenSignatures,
- mSigningDetails.pastSigningCertificates, true);
+ mSigningDetails.getPastSigningCertificates(), true);
serializer.endTag(null, "pastSigs");
}
serializer.endTag(null, tagName);
@@ -106,8 +107,7 @@ class PackageSignatures {
void readXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures)
throws IOException, XmlPullParserException {
- PackageParser.SigningDetails.Builder builder =
- new PackageParser.SigningDetails.Builder();
+ SigningDetails.Builder builder = new SigningDetails.Builder();
final int count = parser.getAttributeInt(null, "count", -1);
if (count == -1) {
@@ -142,13 +142,13 @@ class PackageSignatures {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <sigs> "
+ "unable to convert certificate(s) to public key(s).");
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
}
private int readCertsListXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures,
ArrayList<Signature> signatures, int count, boolean isPastSigs,
- PackageParser.SigningDetails.Builder builder)
+ SigningDetails.Builder builder)
throws IOException, XmlPullParserException {
int pos = 0;
@@ -306,25 +306,25 @@ class PackageSignatures {
buf.append("PackageSignatures{");
buf.append(Integer.toHexString(System.identityHashCode(this)));
buf.append(" version:");
- buf.append(mSigningDetails.signatureSchemeVersion);
+ buf.append(mSigningDetails.getSignatureSchemeVersion());
buf.append(", signatures:[");
- if (mSigningDetails.signatures != null) {
- for (int i = 0; i < mSigningDetails.signatures.length; i++) {
+ if (mSigningDetails.getSignatures() != null) {
+ for (int i = 0; i < mSigningDetails.getSignatures().length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSigningDetails.signatures[i].hashCode()));
+ mSigningDetails.getSignatures()[i].hashCode()));
}
}
buf.append("]");
buf.append(", past signatures:[");
- if (mSigningDetails.pastSigningCertificates != null) {
- for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
+ if (mSigningDetails.getPastSigningCertificates() != null) {
+ for (int i = 0; i < mSigningDetails.getPastSigningCertificates().length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSigningDetails.pastSigningCertificates[i].hashCode()));
+ mSigningDetails.getPastSigningCertificates()[i].hashCode()));
buf.append(" flags: ");
- buf.append(
- Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
+ buf.append(Integer.toHexString(
+ mSigningDetails.getPastSigningCertificates()[i].getFlags()));
}
}
buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c5fbfba9b049..e28540c2beac 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,10 +17,11 @@
package com.android.server.pm;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;
@@ -79,20 +80,20 @@ public final class SELinuxMMAC {
/**
* Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
- * Turning this change off for an app targeting the latest SDK is a no-op.
+ * Turning this change on for an app targeting the latest SDK or higher is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
* TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
*/
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ @Disabled
@ChangeId
static final long SELINUX_LATEST_CHANGES = 143539591L;
/**
* This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
* to R targetSdkVersion enforced changes without changing target SDK. Turning this change
- * off for an app targeting S is a no-op.
+ * off for an app targeting {@code >= android.os.Build.VERSION_CODES.R} is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -364,7 +365,8 @@ public final class SELinuxMMAC {
}
final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
- return android.os.Build.VERSION_CODES.S;
+ return Math.max(
+ android.os.Build.VERSION_CODES.CUR_DEVELOPMENT, pkg.getTargetSdkVersion());
} else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
@@ -577,7 +579,7 @@ final class Policy {
// Check for exact signature matches across all certs.
Signature[] certs = mCerts.toArray(new Signature[0]);
if (pkg.getSigningDetails() != SigningDetails.UNKNOWN
- && !Signature.areExactMatch(certs, pkg.getSigningDetails().signatures)) {
+ && !Signature.areExactMatch(certs, pkg.getSigningDetails().getSignatures())) {
// certs aren't exact match, but the package may have rotated from the known system cert
if (certs.length > 1 || !pkg.getSigningDetails().hasCertificate(certs[0])) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 26aebbc1ea93..f38c48c75b17 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1169,12 +1169,13 @@ public final class Settings implements Watchable, Snappable {
// by that time.
void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) {
// Update signatures if needed.
- if (p.signatures.mSigningDetails.signatures == null) {
+ if (p.signatures.mSigningDetails.getSignatures() == null) {
p.signatures.mSigningDetails = pkg.getSigningDetails();
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
- if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
+ if (p.sharedUser != null
+ && p.sharedUser.signatures.mSigningDetails.getSignatures() == null) {
p.sharedUser.signatures.mSigningDetails = pkg.getSigningDetails();
}
addPackageSettingLPw(p, p.sharedUser);
@@ -2966,6 +2967,17 @@ public final class Settings implements Watchable, Snappable {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+ } finally {
+ if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) {
+ Slog.wtf(PackageManagerService.TAG,
+ "No internal VersionInfo found in settings, using current.");
+ findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+ }
+ if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) {
+ Slog.wtf(PackageManagerService.TAG,
+ "No external VersionInfo found in settings, using current.");
+ findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
+ }
}
// If the build is setup to drop runtime permissions
@@ -4490,7 +4502,7 @@ public final class Settings implements Watchable, Snappable {
pw.print(prefix); pw.print(" versionName="); pw.println(pkg.getVersionName());
pw.print(prefix); pw.print(" usesNonSdkApi="); pw.println(pkg.isUsesNonSdkApi());
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, pkg); pw.println();
- final int apkSigningVersion = pkg.getSigningDetails().signatureSchemeVersion;
+ final int apkSigningVersion = pkg.getSigningDetails().getSignatureSchemeVersion();
pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
pw.print(prefix); pw.print(" applicationInfo=");
pw.println(pkg.toAppInfoToString());
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index fcbf40e29933..945df5dc14ee 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -86,6 +86,7 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.text.TextUtils;
@@ -598,6 +599,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG_PROCSTATE) {
Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleOnUidStateChanged");
synchronized (mLock) {
mUidState.put(uid, procState);
@@ -608,6 +610,7 @@ public class ShortcutService extends IShortcutService.Stub {
mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
private boolean isProcessStateForeground(int processState) {
@@ -703,10 +706,12 @@ public class ShortcutService extends IShortcutService.Stub {
// or anything.
final long start = getStatStartTime();
injectRunOnNewThread(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleUnlockUser");
synchronized (mLock) {
logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
getUserShortcutsLocked(userId);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
});
}
@@ -715,6 +720,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "handleStopUser: user=" + userId);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
synchronized (mLock) {
unloadUserLocked(userId);
@@ -722,6 +728,7 @@ public class ShortcutService extends IShortcutService.Stub {
mUnlockedUsers.put(userId, false);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@GuardedBy("mLock")
@@ -1192,6 +1199,7 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
synchronized (mLock) {
for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = mDirtyUserIds.get(i);
@@ -1205,6 +1213,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
} catch (Exception e) {
wtf("Exception in saveDirtyInfo", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 4ac5be2ec7c7..b8a2dc8f1ef8 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -28,7 +28,6 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -36,8 +35,8 @@ import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -82,6 +81,7 @@ import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -114,6 +114,8 @@ public class StagingManager {
@GuardedBy("mSuccessfulStagedSessionIds")
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
+ private final CompletableFuture<Void> mBootCompleted = new CompletableFuture<>();
+
interface StagedSession {
boolean isMultiPackage();
boolean isApexSession();
@@ -138,25 +140,28 @@ public class StagingManager {
boolean hasParentSessionId();
long getCommittedMillis();
void abandon();
- boolean notifyStartPreRebootVerification();
void notifyEndPreRebootVerification();
void verifySession();
}
- StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
- this(context, packageParserSupplier, ApexManager.getInstance());
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier, Looper looper) {
+ this(context, packageParserSupplier, ApexManager.getInstance(), looper);
}
@VisibleForTesting
StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
ApexManager apexManager) {
+ this(context, packageParserSupplier, apexManager, BackgroundThread.get().getLooper());
+ }
+
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
+ ApexManager apexManager, Looper looper) {
mContext = context;
mPackageParserSupplier = packageParserSupplier;
mApexManager = apexManager;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mPreRebootVerificationHandler = new PreRebootVerificationHandler(
- BackgroundThread.get().getLooper());
+ mPreRebootVerificationHandler = new PreRebootVerificationHandler(looper);
if (mFailureReasonFile.exists()) {
try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) {
@@ -231,7 +236,7 @@ public class StagingManager {
final SigningDetails existingSigningDetails;
try {
existingSigningDetails = ApkSignatureVerifier.verify(
- existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
+ existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
} catch (PackageParserException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
@@ -296,15 +301,6 @@ public class StagingManager {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
}
- final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (activePackage == null) {
- Slog.w(TAG, "Attempting to install new APEX package " + packageInfo.packageName);
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "It is forbidden to install new APEX packages.");
- }
- checkRequiredVersionCode(session, activePackage);
- checkDowngrade(session, activePackage, packageInfo);
result.add(packageInfo);
apexPackageNames.add(packageInfo.packageName);
}
@@ -327,40 +323,6 @@ public class StagingManager {
"Could not find rollback id for commit session: " + sessionId);
}
- private void checkRequiredVersionCode(final StagedSession session,
- final PackageInfo activePackage) throws PackageManagerException {
- if (session.sessionParams().requiredInstalledVersionCode
- == PackageManager.VERSION_CODE_HIGHEST) {
- return;
- }
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- if (activeVersion != session.sessionParams().requiredInstalledVersionCode) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Installed version of APEX package " + activePackage.packageName
- + " does not match required. Active version: " + activeVersion
- + " required: " + session.sessionParams().requiredInstalledVersionCode);
- }
- }
-
- private void checkDowngrade(final StagedSession session,
- final PackageInfo activePackage, final PackageInfo newPackage)
- throws PackageManagerException {
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- final long newVersionCode = newPackage.applicationInfo.longVersionCode;
- final boolean isAppDebuggable = (activePackage.applicationInfo.flags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
- session.sessionParams().installFlags, isAppDebuggable);
- if (activeVersion > newVersionCode && !allowsDowngrade) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Downgrade of APEX package " + newPackage.packageName
- + " is not allowed. Active version: " + activeVersion
- + " attempted: " + newVersionCode);
- }
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
boolean needsCheckpoint) {
@@ -690,6 +652,7 @@ public class StagingManager {
* </ul>
* @throws PackageManagerException if session fails the check
*/
+ // TODO(b/192625695): Rename this method which checks rollbacks in addition to overlapping
@VisibleForTesting
void checkNonOverlappingWithStagedSessions(@NonNull StagedSession session)
throws PackageManagerException {
@@ -723,13 +686,6 @@ public class StagingManager {
continue;
}
- if (stagedSession.getCommittedMillis() > session.getCommittedMillis()) {
- // Ignore sessions that are committed after the provided session. When there are
- // overlaps between sessions, we will fail the one committed later instead of
- // the earlier one.
- continue;
- }
-
// From here on, stagedSession is a parent active staged session
// Check if session is one of the active sessions
@@ -754,15 +710,28 @@ public class StagingManager {
"Session was failed by rollback session: " + session.sessionId());
Slog.i(TAG, "Session " + root.sessionId() + " is marked failed due to "
+ "rollback session: " + session.sessionId());
+ } else if (!isRollback && isRollback(stagedSession)) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_CONFLICT,
+ "Session was failed by rollback session: " + stagedSession.sessionId());
} else if (stagedSession.sessionContains(
s -> s.getPackageName().equals(packageName))) {
- // New session cannot have same package name as one of the active sessions
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Package: " + session.getPackageName() + " in session: "
- + session.sessionId()
- + " has been staged already by session: "
- + stagedSession.sessionId(), null);
+ // Fail the session committed later when there are overlapping packages
+ if (stagedSession.getCommittedMillis() < session.getCommittedMillis()) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Package: " + session.getPackageName() + " in session: "
+ + session.sessionId()
+ + " has been staged already by session: "
+ + stagedSession.sessionId(), null);
+ } else {
+ stagedSession.setSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Package: " + packageName + " in session: "
+ + stagedSession.sessionId()
+ + " has been staged already by session: "
+ + session.sessionId());
+ }
}
// Staging multiple root sessions is not allowed if device doesn't support
@@ -870,7 +839,8 @@ public class StagingManager {
} else if (!session.isSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ Slog.i(TAG, "Restart verification for session=" + session.sessionId());
+ mBootCompleted.thenRun(() -> session.verifySession());
StagedSession session2 = sessions.set(j - 1, session);
sessions.set(i, session2);
j--;
@@ -1059,7 +1029,7 @@ public class StagingManager {
@VisibleForTesting
void onBootCompletedBroadcastReceived() {
- mPreRebootVerificationHandler.readyToStart();
+ mBootCompleted.complete(null);
BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
}
@@ -1100,24 +1070,7 @@ public class StagingManager {
return session;
}
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyVerificationComplete(StagedSession session) {
- mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
- }
-
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyPreRebootVerification_Apk_Complete(@NonNull StagedSession session) {
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
- }
-
private final class PreRebootVerificationHandler extends Handler {
- // Hold sessions before handler gets ready to do the verification.
- private List<StagedSession> mPendingSessions;
- private boolean mIsReady;
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -1131,7 +1084,6 @@ public class StagingManager {
* <p><ul>
* <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
* <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
- * <li>MSG_PRE_REBOOT_VERIFICATION_APK</li>
* <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
* </ul></p>
*
@@ -1139,8 +1091,7 @@ public class StagingManager {
*/
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
- private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
- private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+ private static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
@Override
public void handleMessage(Message msg) {
@@ -1160,9 +1111,6 @@ public class StagingManager {
case MSG_PRE_REBOOT_VERIFICATION_APEX:
handlePreRebootVerification_Apex(session, rollbackId);
break;
- case MSG_PRE_REBOOT_VERIFICATION_APK:
- handlePreRebootVerification_Apk(session);
- break;
case MSG_PRE_REBOOT_VERIFICATION_END:
handlePreRebootVerification_End(session);
break;
@@ -1175,35 +1123,15 @@ public class StagingManager {
}
}
- // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
- private synchronized void readyToStart() {
- mIsReady = true;
- if (mPendingSessions != null) {
- for (int i = 0; i < mPendingSessions.size(); i++) {
- StagedSession session = mPendingSessions.get(i);
- startPreRebootVerification(session);
- }
- mPendingSessions = null;
- }
- }
-
// Method for starting the pre-reboot verification
private synchronized void startPreRebootVerification(
@NonNull StagedSession session) {
- if (!mIsReady) {
- if (mPendingSessions == null) {
- mPendingSessions = new ArrayList<>();
- }
- mPendingSessions.add(session);
- return;
- }
-
- if (session.notifyStartPreRebootVerification()) {
+ mBootCompleted.thenRun(() -> {
int sessionId = session.sessionId();
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
.sendToTarget();
- }
+ });
}
private void onPreRebootVerificationFailure(StagedSession session,
@@ -1232,12 +1160,6 @@ public class StagingManager {
private void notifyPreRebootVerification_Apex_Complete(
@NonNull StagedSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId(), -1, session)
- .sendToTarget();
- }
-
- private void notifyPreRebootVerification_Apk_Complete(
- @NonNull StagedSession session) {
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
.sendToTarget();
}
@@ -1325,19 +1247,6 @@ public class StagingManager {
}
/**
- * Pre-reboot verification state for apk files. Session is sent to
- * {@link PackageManagerService} for verification and it notifies back the result via
- * {@link #notifyPreRebootVerification_Apk_Complete}
- */
- private void handlePreRebootVerification_Apk(@NonNull StagedSession session) {
- if (!session.containsApkSession()) {
- notifyPreRebootVerification_Apk_Complete(session);
- return;
- }
- session.verifySession();
- }
-
- /**
* Pre-reboot verification state for wrapping up:
* <p><ul>
* <li>enables rollback if required</li>
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 9182d811d56d..cc5187ecab2f 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -1,6 +1,22 @@
{
"presubmit": [
{
+ "name": "CtsAtomicInstallTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ]
+ },
+ {
+ "name": "CtsPackageInstallTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ]
+ },
+ {
"name": "CtsUsesLibraryHostTestCases"
},
{
@@ -41,6 +57,53 @@
]
},
{
+ "name": "FrameworksServicesTests",
+ "file_patterns": ["(/|^)ShortcutService\\.java"],
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest1"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest2"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest3"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest4"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest5"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest6"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest7"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest8"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest9"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest10"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest11"
+ }
+ ]
+ },
+ {
+ "name": "CtsShortcutHostTestCases",
+ "file_patterns": ["(/|^)ShortcutService\\.java"]
+ },
+ {
+ "name": "CtsShortcutManagerTestCases",
+ "file_patterns": ["(/|^)ShortcutService\\.java"]
+ },
+ {
"name": "CtsContentTestCases",
"options": [
{
@@ -79,6 +142,19 @@
]
},
{
+ "name": "CtsAppSecurityHostTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.SplitTests"
+ }
+ ]
+ },
+ {
"name": "FrameworksCoreTests",
"options": [
{
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d4feb3a728c8..190e7bcee332 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -117,6 +117,7 @@ import com.android.server.am.UserState;
import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -727,7 +728,7 @@ public class UserManagerService extends IUserManager.Stub {
}
/* Prunes out any partially created or partially removed users. */
- void cleanupPartialUsers() {
+ private void cleanupPartialUsers() {
ArrayList<UserInfo> partials = new ArrayList<>();
synchronized (mUsersLock) {
final int userSize = mUsers.size();
@@ -755,7 +756,7 @@ public class UserManagerService extends IUserManager.Stub {
* Removes any pre-created users from the system. Should be invoked after OTAs, to ensure
* pre-created users are not stale. New pre-created pool can be re-created after the update.
*/
- void cleanupPreCreatedUsers() {
+ private void cleanupPreCreatedUsers() {
final ArrayList<UserInfo> preCreatedUsers;
synchronized (mUsersLock) {
final int userSize = mUsers.size();
@@ -1213,6 +1214,10 @@ public class UserManagerService extends IUserManager.Stub {
private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode,
@Nullable String callingPackage) {
+ Slogf.i(LOG_TAG,
+ "requestQuietModeEnabled called by package %s, with enableQuietMode %b.",
+ callingPackage,
+ enableQuietMode);
UserData userData;
synchronized (mUsersLock) {
userData = getUserDataLU(userId);
@@ -2293,13 +2298,13 @@ public class UserManagerService extends IUserManager.Stub {
// Package private for the inner class.
@GuardedBy("mRestrictionsLock")
- void applyUserRestrictionsLR(@UserIdInt int userId) {
+ private void applyUserRestrictionsLR(@UserIdInt int userId) {
updateUserRestrictionsInternalLR(null, userId);
}
@GuardedBy("mRestrictionsLock")
// Package private for the inner class.
- void applyUserRestrictionsForAllUsersLR() {
+ private void applyUserRestrictionsForAllUsersLR() {
if (DBG) {
debug("applyUserRestrictionsForAllUsersLR");
}
@@ -2900,8 +2905,7 @@ public class UserManagerService extends IUserManager.Stub {
}
@GuardedBy("mUsersLock")
- @VisibleForTesting
- void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
+ private void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
@NonNull ArrayMap<String, UserTypeDetails> userTypes,
final int formerUserTypeVersion,
@NonNull Set<Integer> userIdsToWrite) {
@@ -3880,6 +3884,7 @@ public class UserManagerService extends IUserManager.Stub {
}
/** Checks that the flags do not contain mutually exclusive types/properties. */
+ @VisibleForTesting
static boolean checkUserTypeConsistency(@UserInfoFlag int flags) {
// Mask to check that flags don't refer to multiple user types.
final int userTypeFlagMask = UserInfo.FLAG_GUEST | UserInfo.FLAG_DEMO
@@ -4186,6 +4191,7 @@ public class UserManagerService extends IUserManager.Stub {
return false;
}
+ Slog.i(LOG_TAG, "Removing user " + userId);
addRemovingUserIdLocked(userId);
}
@@ -4308,8 +4314,8 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- void finishRemoveUser(final @UserIdInt int userId) {
- if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId);
+ private void finishRemoveUser(final @UserIdInt int userId) {
+ Slog.i(LOG_TAG, "finishRemoveUser " + userId);
UserInfo user;
synchronized (mUsersLock) {
@@ -4368,6 +4374,7 @@ public class UserManagerService extends IUserManager.Stub {
}
private void removeUserState(final @UserIdInt int userId) {
+ Slog.i(LOG_TAG, "Removing user state of user " + userId);
try {
mContext.getSystemService(StorageManager.class).destroyUserKey(userId);
} catch (IllegalStateException e) {
@@ -4814,8 +4821,15 @@ public class UserManagerService extends IUserManager.Stub {
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("prepareUserData-" + userId);
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+ t.traceEnd();
+
+ t.traceBegin("reconcileAppsData-" + userId);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
+ t.traceEnd();
}
/**
@@ -4984,7 +4998,7 @@ public class UserManagerService extends IUserManager.Stub {
(new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
- int onShellCommand(Shell shell, String cmd) {
+ private int onShellCommand(Shell shell, String cmd) {
if (cmd == null) {
return shell.handleDefaultCommands(cmd);
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 1859b4c83032..01bf63483829 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -608,6 +608,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
private static final int TRON_COMPILATION_REASON_CMDLINE = 22;
+ private static final int TRON_COMPILATION_REASON_PREBUILT = 23;
+ private static final int TRON_COMPILATION_REASON_VDEX = 24;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
@@ -628,6 +630,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
case "shared" : return TRON_COMPILATION_REASON_SHARED;
+ case "prebuilt" : return TRON_COMPILATION_REASON_PREBUILT;
+ case "vdex" : return TRON_COMPILATION_REASON_VDEX;
case "install-fast" :
return TRON_COMPILATION_REASON_INSTALL_FAST;
case "install-bulk" :
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 471a4d3ada46..0d2bcec42667 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -21,8 +21,8 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
import android.content.pm.PermissionGroupInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackageRead;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -200,7 +200,7 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
*/
- PackageParser.SigningDetails getSigningDetails();
+ SigningDetails getSigningDetails();
/**
* TODO(b/135203078): Move split stuff to an inner data class
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 5ee612b6d55f..b30c9dac3705 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -196,6 +196,12 @@ public class AndroidPackageUtils {
}
}
+ if (pkg.getBackupAgentName() != null) {
+ if (Objects.equals(className, pkg.getBackupAgentName())) {
+ return true;
+ }
+ }
+
return false;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 7fbe9533642d..22340220a828 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -22,7 +22,7 @@ import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageImpl;
import android.content.pm.parsing.component.ParsedActivity;
@@ -252,7 +252,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
}
@Override
- public PackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ public PackageImpl setSigningDetails(@Nullable SigningDetails value) {
super.setSigningDetails(value);
return this;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index 657f32c25a89..8e4ee6a4c566 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -17,7 +17,7 @@
package com.android.server.pm.parsing.pkg;
import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
/**
* Methods used for mutation after direct package parsing, mostly done inside
@@ -59,7 +59,7 @@ public interface ParsedPackage extends AndroidPackage {
ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi);
- ParsedPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+ ParsedPackage setSigningDetails(SigningDetails signingDetails);
ParsedPackage setSplitCodePaths(String[] splitCodePaths);
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index cda48063e914..b1b46af8a6e7 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -304,10 +304,6 @@ public final class Permission {
& PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0;
}
- public boolean isDocumenter() {
- return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
- }
-
public boolean isConfigurator() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index dfc14bd733df..27f89e134854 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -87,12 +87,13 @@ import android.content.pm.PackageManager.PermissionGroupInfoFlags;
import android.content.pm.PackageManager.PermissionInfoFlags;
import android.content.pm.PackageManager.PermissionWhitelistFlags;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
@@ -2836,7 +2837,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ if (!isCompatPlatformPermissionForPackage(perm, pkg)) {
shouldGrantNormalPermission = false;
}
}
@@ -3374,14 +3375,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return result;
}
- private static boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
+ private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
boolean allowed = false;
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- for (int ip=0; ip<NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (npi.name.equals(perm)
- && pkg.getTargetSdkVersion() < npi.sdkVersion) {
+ for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+ final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+ if (info.name.equals(perm)
+ && pkg.getTargetSdkVersion() < info.sdkVersion) {
allowed = true;
Log.i(TAG, "Auto-granting " + perm + " to old pkg "
+ pkg.getPackageName());
@@ -3501,15 +3500,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// - or it shares a common signing certificate in its lineage with the defining package,
// and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
- final PackageParser.SigningDetails sourceSigningDetails =
+ final SigningDetails sourceSigningDetails =
getSourcePackageSigningDetails(bp);
return sourceSigningDetails.hasCommonSignerWithCapability(
pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)
+ SigningDetails.CertCapabilities.PERMISSION)
|| pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
|| systemPackage.getSigningDetails().checkCapability(
pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ SigningDetails.CertCapabilities.PERMISSION);
}
private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
@@ -3619,14 +3618,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Special permissions for the device configurator.
allowed = true;
}
- if (!allowed && bp.isDocumenter()
- && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
- PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
- pkg.getPackageName())) {
- // If this permission is to be granted to the documenter and
- // this app is the documenter, then it gets the permission.
- allowed = true;
- }
if (!allowed && bp.isIncidentReportApprover()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
@@ -3667,11 +3658,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@NonNull
- private PackageParser.SigningDetails getSourcePackageSigningDetails(
+ private SigningDetails getSourcePackageSigningDetails(
@NonNull Permission bp) {
final PackageSetting ps = getSourcePackageSetting(bp);
if (ps == null) {
- return PackageParser.SigningDetails.UNKNOWN;
+ return SigningDetails.UNKNOWN;
}
return ps.getSigningDetails();
}
@@ -4227,33 +4218,35 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// If the target package is being uninstalled, we need to revoke this permission
// From all other packages
if (pkg == null || !hasPermission(pkg, bp.getName())) {
- Slog.i(TAG, "Removing permission " + bp.getName()
- + " that used to be declared by " + bp.getPackageName());
- if (bp.isRuntime()) {
- final int[] userIds = mUserManagerInt.getUserIds();
- final int numUserIds = userIds.length;
- for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
- final int userId = userIds[userIdNum];
- mPackageManagerInt.forEachPackage((AndroidPackage p) ->
- revokePermissionFromPackageForUser(p.getPackageName(),
- bp.getName(), true, userId, callback));
- }
- } else {
- mPackageManagerInt.forEachPackage(p -> {
+ if (!isPermissionDeclaredByDisabledSystemPkg(bp)) {
+ Slog.i(TAG, "Removing permission " + bp.getName()
+ + " that used to be declared by " + bp.getPackageName());
+ if (bp.isRuntime()) {
final int[] userIds = mUserManagerInt.getUserIds();
- synchronized (mLock) {
- for (final int userId : userIds) {
- final UidPermissionState uidState = getUidStateLocked(p,
- userId);
- if (uidState == null) {
- Slog.e(TAG, "Missing permissions state for "
- + p.getPackageName() + " and user " + userId);
- continue;
+ final int numUserIds = userIds.length;
+ for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+ final int userId = userIds[userIdNum];
+ mPackageManagerInt.forEachPackage((AndroidPackage p) ->
+ revokePermissionFromPackageForUser(p.getPackageName(),
+ bp.getName(), true, userId, callback));
+ }
+ } else {
+ mPackageManagerInt.forEachPackage(p -> {
+ final int[] userIds = mUserManagerInt.getUserIds();
+ synchronized (mLock) {
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidStateLocked(p,
+ userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for "
+ + p.getPackageName() + " and user " + userId);
+ continue;
+ }
+ uidState.removePermissionState(bp.getName());
}
- uidState.removePermissionState(bp.getName());
}
- }
- });
+ });
+ }
}
synchronized (mLock) {
mRegistry.removePermission(bp.getName());
@@ -4278,6 +4271,22 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return changed;
}
+ private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) {
+ final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
+ permission.getPackageName());
+ if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) {
+ final String permissionName = permission.getName();
+ final List<ParsedPermission> sourcePerms = disabledSourcePs.getPkg().getPermissions();
+ for (ParsedPermission sourcePerm : sourcePerms) {
+ if (TextUtils.equals(permissionName, sourcePerm.getName())
+ && permission.getProtectionLevel() == sourcePerm.getProtectionLevel()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Revoke a runtime permission from a package for a given user ID.
*/
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index adf8f0d3e486..844111a8fab1 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -182,10 +182,10 @@ public class DomainVerificationDebug {
if (!reusedMap.isEmpty()) {
if (!wasHeaderPrinted) {
- Signature[] signatures = pkg.getSigningDetails().signatures;
+ Signature[] signatures = pkg.getSigningDetails().getSignatures();
String signaturesDigest = signatures == null ? null : Arrays.toString(
PackageUtils.computeSignaturesSha256Digests(
- pkg.getSigningDetails().signatures, ":"));
+ pkg.getSigningDetails().getSignatures(), ":"));
writer.println(pkgState.getPackageName() + ":");
writer.increaseIndent();
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 3c9b1063ba93..04b5005aa283 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -16,10 +16,8 @@
package com.android.server.policy;
-import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
-import android.hardware.ICameraService;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManagerInternal;
@@ -27,13 +25,11 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDisplayFoldListener;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
-import com.android.server.camera.CameraServiceProxy;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -41,13 +37,8 @@ import com.android.server.wm.WindowManagerInternal;
* TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
*/
class DisplayFoldController {
- private static final String TAG = "DisplayFoldController";
-
private final WindowManagerInternal mWindowManagerInternal;
private final DisplayManagerInternal mDisplayManagerInternal;
- // Camera service proxy can be disabled through a config.
- @Nullable
- private final CameraServiceProxy mCameraServiceProxy;
private final int mDisplayId;
private final Handler mHandler;
@@ -64,12 +55,10 @@ class DisplayFoldController {
DisplayFoldController(
Context context, WindowManagerInternal windowManagerInternal,
- DisplayManagerInternal displayManagerInternal,
- @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea,
+ DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
Handler handler) {
mWindowManagerInternal = windowManagerInternal;
mDisplayManagerInternal = displayManagerInternal;
- mCameraServiceProxy = cameraServiceProxy;
mDisplayId = displayId;
mFoldedArea = new Rect(foldedArea);
mHandler = handler;
@@ -124,16 +113,6 @@ class DisplayFoldController {
}
}
- if (mCameraServiceProxy != null) {
- if (folded) {
- mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
- } else {
- mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
- }
- } else {
- Slog.w(TAG, "Camera service unavailable to toggle folded state.");
- }
-
mDurationLogger.setDeviceFolded(folded);
mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
mFolded = folded;
@@ -188,8 +167,6 @@ class DisplayFoldController {
LocalServices.getService(WindowManagerInternal.class);
final DisplayManagerInternal displayService =
LocalServices.getService(DisplayManagerInternal.class);
- final CameraServiceProxy cameraServiceProxy =
- LocalServices.getService(CameraServiceProxy.class);
final String configFoldedArea = context.getResources().getString(
com.android.internal.R.string.config_foldedArea);
@@ -201,6 +178,6 @@ class DisplayFoldController {
}
return new DisplayFoldController(context, windowManagerService, displayService,
- cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler());
+ displayId, foldedArea, DisplayThread.getHandler());
}
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 89d54158b006..ad435146f579 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -71,6 +71,7 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
+import com.android.server.utils.TimingsTraceAndSlog;
import java.util.ArrayList;
import java.util.Collections;
@@ -365,7 +366,11 @@ public final class PermissionPolicyService extends SystemService {
return;
}
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("Permission_grant_default_permissions-" + userId);
grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
+ t.traceEnd();
final OnInitializedCallback callback;
@@ -375,11 +380,15 @@ public final class PermissionPolicyService extends SystemService {
}
// Force synchronization as permissions might have changed
+ t.traceBegin("Permission_synchronize_permissions-" + userId);
synchronizePermissionsAndAppOpsForUser(userId);
+ t.traceEnd();
// Tell observers we are initialized for this user.
if (callback != null) {
+ t.traceBegin("Permission_onInitialized-" + userId);
callback.onInitialized(userId);
+ t.traceEnd();
}
}
@@ -394,6 +403,7 @@ public final class PermissionPolicyService extends SystemService {
private void grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@UserIdInt int userId) {
if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPermsIfNeeded(" + userId + ")");
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final PackageManagerInternal packageManagerInternal =
LocalServices.getService(PackageManagerInternal.class);
@@ -426,9 +436,12 @@ public final class PermissionPolicyService extends SystemService {
}
});
try {
+ t.traceBegin("Permission_callback_waiting-" + userId);
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
+ } finally {
+ t.traceEnd();
}
permissionControllerManager.updateUserSensitive();
@@ -494,14 +507,19 @@ public final class PermissionPolicyService extends SystemService {
*/
private void synchronizePermissionsAndAppOpsForUser(@UserIdInt int userId) {
if (DEBUG) Slog.i(LOG_TAG, "synchronizePermissionsAndAppOpsForUser(" + userId + ")");
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
getUserContext(getContext(), UserHandle.of(userId)));
+ t.traceBegin("Permission_synchronize_addPackages-" + userId);
packageManagerInternal.forEachPackage(
(pkg) -> synchronizer.addPackage(pkg.getPackageName()));
+ t.traceEnd();
+ t.traceBegin("Permission_syncPackages-" + userId);
synchronizer.syncPackages();
+ t.traceEnd();
}
private void resetAppOpPermissionsIfNotRequestedForUidAsync(int uid) {
diff --git a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
index c2596c74146a..c08faf8ea7d3 100644
--- a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
+++ b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
@@ -323,7 +323,7 @@ public class RoleServicePlatformHelperImpl implements RoleServicePlatformHelper
dataOutputStream.writeUTF(disabledComponents.valueAt(i));
}
- for (final Signature signature : pkg.getSigningDetails().signatures) {
+ for (final Signature signature : pkg.getSigningDetails().getSignatures()) {
dataOutputStream.write(signature.toByteArray());
}
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9638255dfc79..c39b5da51486 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -352,6 +352,12 @@ public final class PowerManagerService extends SystemService
// requested because it is updated asynchronously by the display power controller.
private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
+ // The suspend blocker used to keep the CPU alive while the device is booting.
+ private final SuspendBlocker mBootingSuspendBlocker;
+
+ // True if the wake lock suspend blocker has been acquired.
+ private boolean mHoldingBootingSuspendBlocker;
+
// The suspend blocker used to keep the CPU alive when an application has acquired
// a wake lock.
private final SuspendBlocker mWakeLockSuspendBlocker;
@@ -1036,10 +1042,16 @@ public final class PowerManagerService extends SystemService
}
synchronized (mLock) {
+ mBootingSuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Booting");
mWakeLockSuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
mDisplaySuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+ if (mBootingSuspendBlocker != null) {
+ mBootingSuspendBlocker.acquire();
+ mHoldingBootingSuspendBlocker = true;
+ }
if (mDisplaySuspendBlocker != null) {
mDisplaySuspendBlocker.acquire();
mHoldingDisplaySuspendBlocker = true;
@@ -3385,6 +3397,10 @@ public final class PowerManagerService extends SystemService
}
// First acquire suspend blockers if needed.
+ if (!mBootCompleted && !mHoldingBootingSuspendBlocker) {
+ mBootingSuspendBlocker.acquire();
+ mHoldingBootingSuspendBlocker = true;
+ }
if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.acquire();
mHoldingWakeLockSuspendBlocker = true;
@@ -3411,6 +3427,10 @@ public final class PowerManagerService extends SystemService
}
// Then release suspend blockers if needed.
+ if (mBootCompleted && mHoldingBootingSuspendBlocker) {
+ mBootingSuspendBlocker.release();
+ mHoldingBootingSuspendBlocker = false;
+ }
if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.release();
mHoldingWakeLockSuspendBlocker = false;
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index f72adb609f6f..13218731af70 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -970,6 +970,12 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
final long token = Binder.clearCallingIdentity();
try {
CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
+ if (apexInfoList == null) {
+ Log.i(TAG, "apex_info.pb not present in OTA package. "
+ + "Assuming device doesn't support compressed"
+ + "APEX, continueing without allocating space.");
+ return true;
+ }
ApexManager apexManager = ApexManager.getInstance();
apexManager.reserveSpaceForCompressedApex(apexInfoList);
return true;
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index 3d78828888da..141d4dcf77d0 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -44,6 +44,8 @@ public class RecoverySystemShellCommand extends ShellCommand {
return requestLskf();
case "clear-lskf":
return clearLskf();
+ case "is-lskf-captured":
+ return isLskfCaptured();
case "reboot-and-apply":
return rebootAndApply();
default:
@@ -74,6 +76,14 @@ public class RecoverySystemShellCommand extends ShellCommand {
return 0;
}
+ private int isLskfCaptured() throws RemoteException {
+ String packageName = getNextArgRequired();
+ boolean captured = mService.isLskfCaptured(packageName);
+ PrintWriter pw = getOutPrintWriter();
+ pw.printf("%s LSKF capture status: %s\n", packageName, captured ? "true" : "false");
+ return 0;
+ }
+
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
@@ -90,8 +100,9 @@ public class RecoverySystemShellCommand extends ShellCommand {
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("Recovery system commands:");
- pw.println(" request-lskf <token>");
+ pw.println(" request-lskf <package_name>");
pw.println(" clear-lskf");
- pw.println(" reboot-and-apply <token> <reason>");
+ pw.println(" is-lskf-captured <package_name>");
+ pw.println(" reboot-and-apply <package_name> <reason>");
}
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 9560f59924de..7bf3478ac49e 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -116,10 +116,10 @@ class Rollback {
static final int ROLLBACK_STATE_DELETED = 4;
/**
- * The session ID for the staged session if this rollback data represents a staged session,
- * {@code -1} otherwise.
+ * The session ID associate with this rollback. This is the parent session ID in the case of
+ * a multi-package session.
*/
- private final int mStagedSessionId;
+ private final int mOriginalSessionId;
/**
* The rollback info for this rollback.
@@ -181,13 +181,6 @@ class Rollback {
private final int[] mPackageSessionIds;
/**
- * The number of sessions in the install which are notified with success by
- * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
- * This rollback will be enabled only after all child sessions finished with success.
- */
- private int mNumPackageSessionsWithSuccess;
-
- /**
* The extension versions supported at the time of rollback creation.
*/
@NonNull private final SparseIntArray mExtensionVersions;
@@ -203,24 +196,25 @@ class Rollback {
*
* @param rollbackId the id of the rollback.
* @param backupDir the directory where the rollback data is stored.
- * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
+ * @param originalSessionId the session id associated with this rollback.
+ * @param isStaged true if this is a staged rollback.
* @param userId the user that performed the install with rollback enabled.
* @param installerPackageName the installer package name from the original install session.
* @param packageSessionIds the session ids for all packages in the install.
* @param extensionVersions the extension versions supported at the time of rollback creation
*/
- Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
+ Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId,
String installerPackageName, int[] packageSessionIds,
SparseIntArray extensionVersions) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
- /* isStaged */ stagedSessionId != -1,
+ /* isStaged */ isStaged,
/* causePackages */ new ArrayList<>(),
/* committedSessionId */ -1);
mUserId = userId;
mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
- mStagedSessionId = stagedSessionId;
+ mOriginalSessionId = originalSessionId;
mState = ROLLBACK_STATE_ENABLING;
mTimestamp = Instant.now();
mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
@@ -228,16 +222,10 @@ class Rollback {
mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null;
}
- Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
- String installerPackageName) {
- this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null,
- new SparseIntArray(0));
- }
-
/**
* Constructs a pre-populated Rollback instance.
*/
- Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
+ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId,
@RollbackState int state, String stateDescription, boolean restoreUserDataInProgress,
int userId, String installerPackageName, SparseIntArray extensionVersions) {
this.info = info;
@@ -245,7 +233,7 @@ class Rollback {
mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
mTimestamp = timestamp;
- mStagedSessionId = stagedSessionId;
+ mOriginalSessionId = originalSessionId;
mState = state;
mStateDescription = stateDescription;
mRestoreUserDataInProgress = restoreUserDataInProgress;
@@ -298,12 +286,11 @@ class Rollback {
}
/**
- * Returns the session ID for the staged session if this rollback data represents a staged
- * session, or {@code -1} otherwise.
+ * Returns the session ID associated with this rollback, or {@code -1} if unknown.
*/
@AnyThread
- int getStagedSessionId() {
- return mStagedSessionId;
+ int getOriginalSessionId() {
+ return mOriginalSessionId;
}
/**
@@ -838,17 +825,6 @@ class Rollback {
}
/**
- * Called when a child session finished with success.
- * Returns true when all child sessions are notified with success. This rollback will be
- * enabled only after all child sessions finished with success.
- */
- @WorkerThread
- boolean notifySessionWithSuccess() {
- assertInWorkerThread();
- return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
- }
-
- /**
* Returns true if all packages in this rollback are enabled. We won't enable this rollback
* until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
* automatically when the embedding apex is enabled.
@@ -954,9 +930,8 @@ class Rollback {
ipw.println("-state: " + getStateAsString());
ipw.println("-stateDescription: " + mStateDescription);
ipw.println("-timestamp: " + getTimestamp());
- if (getStagedSessionId() != -1) {
- ipw.println("-stagedSessionId: " + getStagedSessionId());
- }
+ ipw.println("-isStaged: " + isStaged());
+ ipw.println("-originalSessionId: " + getOriginalSessionId());
ipw.println("-packages:");
ipw.increaseIndent();
for (PackageRollbackInfo pkg : info.getPackages()) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9e19f57ed0e2..f7ed000ac403 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -247,11 +247,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
PackageManagerInternal.class);
pm.setEnableRollbackCode(token, ret);
});
-
- // We're handling the ordered broadcast. Abort the
- // broadcast because there is no need for it to go to
- // anyone else.
- abortBroadcast();
}
}
}, enableRollbackFilter, null, getHandler());
@@ -611,11 +606,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
PackageInstaller.SessionInfo session = mContext.getPackageManager()
- .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
+ .getPackageInstaller().getSessionInfo(rollback.getOriginalSessionId());
if (session == null || session.isStagedSessionFailed()) {
if (rollback.isEnabling()) {
iter.remove();
- deleteRollback(rollback, "Session " + rollback.getStagedSessionId()
+ deleteRollback(rollback, "Session " + rollback.getOriginalSessionId()
+ " not existed or failed");
}
continue;
@@ -789,7 +784,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
newRollback = createNewRollback(parentSession);
}
- return enableRollbackForPackageSession(newRollback, packageSession);
+ if (enableRollbackForPackageSession(newRollback, packageSession)) {
+ // Persist the rollback if all packages are enabled. We will make the rollback
+ // available once the whole session is installed successfully.
+ return newRollback.allPackagesEnabled() ? completeEnableRollback(newRollback) : true;
+ } else {
+ return false;
+ }
}
@WorkerThread
@@ -978,43 +979,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
throw new SecurityException("notifyStagedSession may only be called by the system.");
}
- // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
- // to preserve the invariant that all operations that modify state happen there.
return awaitResult(() -> {
assertInWorkerThread();
- PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
-
- final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
- if (session == null) {
- Slog.e(TAG, "No matching install session for: " + sessionId);
- return -1;
- }
-
- Rollback newRollback = createNewRollback(session);
- if (!session.isMultiPackage()) {
- if (!enableRollbackForPackageSession(newRollback, session)) {
- Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
- }
- } else {
- for (int childSessionId : session.getChildSessionIds()) {
- final PackageInstaller.SessionInfo childSession =
- installer.getSessionInfo(childSessionId);
- if (childSession == null) {
- Slog.e(TAG, "No matching child install session for: " + childSessionId);
- break;
- }
- if (!enableRollbackForPackageSession(newRollback, childSession)) {
- Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
- break;
- }
- }
- }
-
- if (!completeEnableRollback(newRollback)) {
- return -1;
- } else {
- return newRollback.info.getRollbackId();
- }
+ Rollback rollback = getRollbackForSession(sessionId);
+ return rollback != null ? rollback.info.getRollbackId() : -1;
});
}
@@ -1124,21 +1092,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
}
+ Rollback rollback = getRollbackForSession(sessionId);
+ if (rollback == null || !rollback.isEnabling()
+ || sessionId != rollback.getOriginalSessionId()) {
+ // We only care about the parent session id which will tell us whether the
+ // whole session is successful or not.
+ return;
+ }
if (success) {
- Rollback rollback = getRollbackForSession(sessionId);
- if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
- && rollback.notifySessionWithSuccess()
- && completeEnableRollback(rollback)) {
+ if (!rollback.isStaged() && completeEnableRollback(rollback)) {
+ // completeEnableRollback() ensures the rollback is deleted if not all packages
+ // are enabled. For staged rollbacks, we will make them available in
+ // onBootCompleted().
makeRollbackAvailable(rollback);
}
} else {
- Rollback rollback = getRollbackForSession(sessionId);
- if (rollback != null && rollback.isEnabling()) {
- Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
- + " for failed session id=" + sessionId);
- mRollbacks.remove(rollback);
- deleteRollback(rollback, "Session " + sessionId + " failed");
- }
+ Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+ + " for failed session id=" + sessionId);
+ mRollbacks.remove(rollback);
+ deleteRollback(rollback, "Session " + sessionId + " failed");
}
}
}
@@ -1307,7 +1279,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName, packageSessionIds, getExtensionVersions());
} else {
- rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
+ rollback = mRollbackStore.createNonStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName, packageSessionIds, getExtensionVersions());
}
@@ -1339,7 +1311,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
// We expect mRollbacks to be a very small list; linear search should be plenty fast.
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.getStagedSessionId() == sessionId
+ if (rollback.getOriginalSessionId() == sessionId
|| rollback.containsSessionId(sessionId)) {
return rollback;
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index fc62f5be801c..6b783f709810 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -209,23 +209,24 @@ class RollbackStore {
* Creates a new Rollback instance for a non-staged rollback with
* backupDir assigned.
*/
- Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
- int[] packageSessionIds, SparseIntArray extensionVersions) {
+ Rollback createNonStagedRollback(int rollbackId, int originalSessionId, int userId,
+ String installerPackageName, int[] packageSessionIds,
+ SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
- packageSessionIds, extensionVersions);
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ false, userId,
+ installerPackageName, packageSessionIds, extensionVersions);
}
/**
* Creates a new Rollback instance for a staged rollback with
* backupDir assigned.
*/
- Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
+ Rollback createStagedRollback(int rollbackId, int originalSessionId, int userId,
String installerPackageName, int[] packageSessionIds,
SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
- packageSessionIds, extensionVersions);
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, userId,
+ installerPackageName, packageSessionIds, extensionVersions);
}
private static boolean isLinkPossible(File oldFile, File newFile) {
@@ -312,7 +313,7 @@ class RollbackStore {
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
dataJson.put("timestamp", rollback.getTimestamp().toString());
- dataJson.put("stagedSessionId", rollback.getStagedSessionId());
+ dataJson.put("originalSessionId", rollback.getOriginalSessionId());
dataJson.put("state", rollback.getStateAsString());
dataJson.put("stateDescription", rollback.getStateDescription());
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
@@ -380,7 +381,9 @@ class RollbackStore {
rollbackInfoFromJson(dataJson.getJSONObject("info")),
backupDir,
Instant.parse(dataJson.getString("timestamp")),
- dataJson.getInt("stagedSessionId"),
+ // Backward compatibility: Historical rollbacks are not erased upon OTA update.
+ // Need to load the old field 'stagedSessionId' as fallback.
+ dataJson.optInt("originalSessionId", dataJson.optInt("stagedSessionId", -1)),
rollbackStateFromString(dataJson.getString("state")),
dataJson.optString("stateDescription"),
dataJson.getBoolean("restoreUserDataInProgress"),
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index a58291425d4c..82ba60fa3ce5 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -53,17 +53,15 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
private static final String TAG = RemoteRotationResolverService.class.getSimpleName();
private final long mIdleUnbindTimeoutMs;
- private final Object mLock;
RemoteRotationResolverService(Context context, ComponentName serviceName,
- int userId, long idleUnbindTimeoutMs, Object lock) {
+ int userId, long idleUnbindTimeoutMs) {
super(context,
new Intent(RotationResolverService.SERVICE_INTERFACE).setComponent(serviceName),
BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
IRotationResolverService.Stub::asInterface);
mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
- mLock = lock;
// Bind right away.
connect();
@@ -75,8 +73,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
return -1;
}
- @GuardedBy("mLock")
- public void resolveRotationLocked(RotationRequest request) {
+ public void resolveRotation(RotationRequest request) {
final RotationResolutionRequest remoteRequest = request.mRemoteRequest;
post(service -> service.resolveRotation(request.mIRotationResolverCallback, remoteRequest));
@@ -97,13 +94,15 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
@NonNull
private final IRotationResolverCallback mIRotationResolverCallback;
@NonNull
- private ICancellationSignal mCancellation;
- @NonNull
private final CancellationSignal mCancellationSignalInternal;
@NonNull
final RotationResolverInternal.RotationResolverCallbackInternal
mCallbackInternal;
+ @NonNull
+ @GuardedBy("mLock")
+ private ICancellationSignal mCancellation;
+
@GuardedBy("mLock")
boolean mIsFulfilled;
@@ -111,17 +110,19 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
final RotationResolutionRequest mRemoteRequest;
boolean mIsDispatched;
- private final Object mLock = new Object();
+ private final Object mLock;
private final long mRequestStartTimeMillis;
RotationRequest(
@NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
- RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal) {
+ RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal,
+ Object lock) {
mCallbackInternal = callbackInternal;
mRemoteRequest = request;
mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
+ mLock = lock;
}
@@ -153,7 +154,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
}
private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
- private WeakReference<RotationRequest> mRequestWeakReference;
+ private final WeakReference<RotationRequest> mRequestWeakReference;
RotationResolverCallback(RotationRequest request) {
this.mRequestWeakReference = new WeakReference<>(request);
@@ -215,7 +216,6 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
}
}
}
-
}
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 165a1d617429..48d8fed32aca 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -121,26 +121,26 @@ final class RotationResolverManagerPerUserService extends
final RotationResolverInternal.RotationResolverCallbackInternal wrapper =
new RotationResolverInternal.RotationResolverCallbackInternal() {
- @Override
- public void onSuccess(int result) {
- synchronized (mLock) {
- mLatencyTracker
- .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
- }
- callbackInternal.onSuccess(result);
- }
-
- @Override
- public void onFailure(int error) {
- synchronized (mLock) {
- mLatencyTracker
- .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
- }
- callbackInternal.onFailure(error);
- }
- };
+ @Override
+ public void onSuccess(int result) {
+ synchronized (mLock) {
+ mLatencyTracker
+ .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+ }
+ callbackInternal.onSuccess(result);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ synchronized (mLock) {
+ mLatencyTracker
+ .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+ }
+ callbackInternal.onFailure(error);
+ }
+ };
mCurrentRequest = new RemoteRotationResolverService.RotationRequest(wrapper,
- request, cancellationSignalInternal);
+ request, cancellationSignalInternal, mLock);
cancellationSignalInternal.setOnCancelListener(() -> {
synchronized (mLock) {
@@ -152,7 +152,7 @@ final class RotationResolverManagerPerUserService extends
});
- mRemoteService.resolveRotationLocked(mCurrentRequest);
+ mRemoteService.resolveRotation(mCurrentRequest);
mCurrentRequest.mIsDispatched = true;
}
@@ -160,7 +160,7 @@ final class RotationResolverManagerPerUserService extends
private void ensureRemoteServiceInitiated() {
if (mRemoteService == null) {
mRemoteService = new RemoteRotationResolverService(getContext(), mComponentName,
- getUserId(), CONNECTION_TTL_MILLIS, mLock);
+ getUserId(), CONNECTION_TTL_MILLIS);
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 6366280e1762..e006b6525526 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -21,24 +21,23 @@ import android.annotation.Nullable;
import android.hardware.audio.common.V2_0.Uuid;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.Properties;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
import android.media.audio.common.AudioConfig;
import android.media.audio.common.AudioOffloadInfo;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.HidlMemory;
import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
@@ -55,9 +54,9 @@ import java.util.regex.Matcher;
*/
class ConversionUtil {
static @NonNull
- SoundTriggerModuleProperties hidl2aidlProperties(
+ Properties hidl2aidlProperties(
@NonNull ISoundTriggerHw.Properties hidlProperties) {
- SoundTriggerModuleProperties aidlProperties = new SoundTriggerModuleProperties();
+ Properties aidlProperties = new Properties();
aidlProperties.implementor = hidlProperties.implementor;
aidlProperties.description = hidlProperties.description;
aidlProperties.version = hidlProperties.version;
@@ -75,9 +74,9 @@ class ConversionUtil {
return aidlProperties;
}
- static @NonNull SoundTriggerModuleProperties hidl2aidlProperties(
- @NonNull Properties hidlProperties) {
- SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
+ static @NonNull Properties hidl2aidlProperties(
+ @NonNull android.hardware.soundtrigger.V2_3.Properties hidlProperties) {
+ Properties aidlProperties = hidl2aidlProperties(hidlProperties.base);
aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
aidlProperties.audioCapabilities =
hidl2aidlAudioCapabilities(hidlProperties.audioCapabilities);
@@ -216,9 +215,11 @@ class ConversionUtil {
}
static @NonNull android.hardware.soundtrigger.V2_3.RecognitionConfig aidl2hidlRecognitionConfig(
- @NonNull RecognitionConfig aidlConfig) {
+ @NonNull RecognitionConfig aidlConfig, int deviceHandle, int ioHandle) {
android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
new android.hardware.soundtrigger.V2_3.RecognitionConfig();
+ hidlConfig.base.header.captureDevice = deviceHandle;
+ hidlConfig.base.header.captureHandle = ioHandle;
hidlConfig.base.header.captureRequested = aidlConfig.captureRequested;
for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) {
hidlConfig.base.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
@@ -299,8 +300,6 @@ class ConversionUtil {
aidlEvent.status = hidl2aidlRecognitionStatus(hidlEvent.status);
aidlEvent.type = hidl2aidlSoundModelType(hidlEvent.type);
aidlEvent.captureAvailable = hidlEvent.captureAvailable;
- // hidlEvent.captureSession is never a valid field.
- aidlEvent.captureSession = -1;
aidlEvent.captureDelayMs = hidlEvent.captureDelayMs;
aidlEvent.capturePreambleMs = hidlEvent.capturePreambleMs;
aidlEvent.triggerInData = hidlEvent.triggerInData;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
new file mode 100644
index 000000000000..2f2cb594ff3a
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
@@ -0,0 +1,120 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.os.HwBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This is the basic implementation of HalFactory, which uses either the default STHAL or a mock.
+ *
+ * The choice of which HAL to use is as follows:
+ * - Get the (int) value of "debug.soundtrigger_middleware.use_mock_hal" sysprop, if it doesn't
+ * exist, assume 0.
+ * - If the value is 0, use the default HAL on the device. Connect to the latest-version "default"
+ * instance declared in the device manifest (either AIDL or HIDL).
+ * - If the value is 2, connect to a "mock" instance of the latest v2.x (HIDL).
+ * - If the value is 3, connect to a "mock" instance of soundtrigger3 (AIDL).
+ * - Otherwise, throw.
+ */
+class DefaultHalFactory implements HalFactory {
+ private static final String TAG = "SoundTriggerMiddlewareDefaultHalFactory";
+
+ private static final @NonNull ICaptureStateNotifier mCaptureStateNotifier =
+ new ExternalCaptureStateTracker();
+
+ private static final int USE_DEFAULT_HAL = 0;
+ private static final int USE_MOCK_HAL_V2 = 2;
+ private static final int USE_MOCK_HAL_V3 = 3;
+
+ @Override
+ public ISoundTriggerHal create() {
+ try {
+ int mockHal = SystemProperties.getInt("debug.soundtrigger_middleware.use_mock_hal",
+ USE_DEFAULT_HAL);
+ if (mockHal == USE_DEFAULT_HAL) {
+ // Use production HAL.
+
+ // Try soundtrigger3 (AIDL) first.
+ final String aidlServiceName =
+ android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ + "/default";
+ if (ServiceManager.isDeclared(aidlServiceName)) {
+ Log.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
+ return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+ () -> {
+ // This property needs to be defined in an init.rc script and
+ // trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ });
+ }
+
+ // Fallback to soundtrigger-V2.x (HIDL).
+ Log.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
+ ISoundTriggerHw driver = ISoundTriggerHw.getService(true);
+ return SoundTriggerHw2Compat.create(driver, () -> {
+ // This property needs to be defined in an init.rc script and
+ // trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ }, mCaptureStateNotifier);
+ } else if (mockHal == USE_MOCK_HAL_V2) {
+ // Use V2 mock.
+ Log.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
+ HwBinder.setTrebleTestingOverride(true);
+ try {
+ ISoundTriggerHw driver = ISoundTriggerHw.getService("mock", true);
+ return SoundTriggerHw2Compat.create(driver, () -> {
+ try {
+ driver.debug(null, new ArrayList<>(Arrays.asList("reboot")));
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reboot mock HAL", e);
+ }
+ }, mCaptureStateNotifier);
+ } finally {
+ HwBinder.setTrebleTestingOverride(false);
+ }
+ } else if (mockHal == USE_MOCK_HAL_V3) {
+ // Use V3 mock.
+ final String aidlServiceName =
+ android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ + "/mock";
+ Log.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
+ return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+ () -> {
+ try {
+ ServiceManager.waitForService(aidlServiceName).shellCommand(null,
+ null, null, new String[]{"reboot"}, null, null);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reboot mock HAL", e);
+ }
+ });
+ } else {
+ throw new RuntimeException("Unknown HAL mock version: " + mockHal);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index 940490411b49..d195fbedcf2f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -16,42 +16,55 @@
package com.android.server.soundtrigger_middleware;
+import android.annotation.NonNull;
import android.util.Log;
+import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.Semaphore;
-import java.util.function.Consumer;
/**
* This is a never-give-up listener for sound trigger external capture state notifications, as
* published by the audio policy service.
*
* This class will constantly try to connect to the service over a background thread and tolerate
- * its death. The client will be notified by a single provided function that is called in a
- * synchronized manner.
- * For simplicity, there is currently no way to stop the tracker. This is possible to add if the
- * need ever arises.
+ * its death.
*/
-class ExternalCaptureStateTracker {
+class ExternalCaptureStateTracker implements ICaptureStateNotifier {
private static final String TAG = "CaptureStateTracker";
- /** Our client's listener. */
- private final Consumer<Boolean> mListener;
+
+ /** Our client's listeners. Also used as lock. */
+ private final List<Listener> mListeners = new LinkedList<>();
+
+ /** Conservatively, until notified otherwise, we assume capture is active. */
+ private boolean mCaptureActive = true;
+
/** This semaphore will get a permit every time we need to reconnect. */
private final Semaphore mNeedToConnect = new Semaphore(1);
/**
* Constructor. Will start a background thread to do the work.
- *
- * @param listener A client provided listener that will be called on state
- * changes. May be
- * called multiple consecutive times with the same value. Never
- * called
- * concurrently.
*/
- ExternalCaptureStateTracker(Consumer<Boolean> listener) {
- mListener = listener;
+ ExternalCaptureStateTracker() {
new Thread(this::run).start();
}
+
+ @Override
+ public boolean registerListener(@NonNull Listener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ return mCaptureActive;
+ }
+ }
+
+ @Override
+ public void unregisterListener(Listener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
/**
* Routine for the background thread. Keeps trying to reconnect.
*/
@@ -74,7 +87,12 @@ class ExternalCaptureStateTracker {
*/
private void setCaptureState(boolean active) {
try {
- mListener.accept(active);
+ synchronized (mListeners) {
+ mCaptureActive = active;
+ for (Listener listener : mListeners) {
+ listener.onCaptureStateChange(active);
+ }
+ }
} catch (Exception e) {
Log.e(TAG, "Exception caught while setting capture state", e);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
index b19e2ed1a91b..6da8a795991f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
@@ -16,16 +16,14 @@
package com.android.server.soundtrigger_middleware;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
-
/**
- * A factory for creating instances of {@link ISoundTriggerHw}.
+ * A factory for creating instances of {@link ISoundTriggerHal}.
*
* @hide
*/
public interface HalFactory {
/**
- * @return An instance of {@link ISoundTriggerHw}.
+ * @return An instance of {@link ISoundTriggerHal}.
*/
- ISoundTriggerHw create();
+ ISoundTriggerHal create();
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
new file mode 100644
index 000000000000..07d83cabcf83
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * Allow registering listeners for tracking changes in audio capture state (when recording starts /
+ * stops). The client will be notified in a synchronized manner.
+ */
+interface ICaptureStateNotifier {
+ interface Listener {
+ void onCaptureStateChange(boolean state);
+ }
+
+ /**
+ * Register a listener for state change notifications. Returns the current capture state and
+ * any subsequent changes will be sent to the listener.
+ * @param listener The listener.
+ * @return The state at the time of registration.
+ */
+ boolean registerListener(@NonNull Listener listener);
+
+ /**
+ * Unregister a listener, previously registered with {@link #registerListener(Listener)}.
+ * Once this call returns, no more invocations of the listener will be made.
+ */
+ void unregisterListener(@NonNull Listener listener);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
new file mode 100644
index 000000000000..aa85dd01405e
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
+
+/**
+ * This interface mimics the soundtrigger HAL interface, with a few key differences:
+ * <ul>
+ * <li>Generally, methods should not throw, except for the following cases:
+ * <ul>
+ * <li>Any unexpected HAL behavior is considered an internal HAL malfunction and should be thrown
+ * as a {@link HalException}, from any method.
+ * <li>A {@link RuntimeException} with a {@link android.os.DeadObjectException} cause represents
+ * a dead HAL process and may be thrown by any method.
+ * <li>Implementations of earlier versions of the interface may throw a
+ * {@link RecoverableException} with a
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED} for methods that
+ * have been introduced in later versions of the interface.
+ * <li>Certain methods are allowed to throw a {@link RecoverableException} with a
+ * {@link android.media.soundtrigger.Status#RESOURCE_CONTENTION} to indicate transient
+ * failures.
+ * </ul>
+ * <li>Some binder-specific details are hidden.
+ * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
+ * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
+ * them.
+ * </ul>
+ * For cases where the client wants to explicitly handle specific versions of the underlying driver
+ * interface, they may call {@link #interfaceDescriptor()}.
+ * <p>
+ * <b>Note to maintainers</b>: This class must always be kept in sync with the latest version,
+ * so that clients have access to the entire functionality without having to burden themselves with
+ * compatibility, as much as possible.
+ */
+interface ISoundTriggerHal {
+ /**
+ * @see ISoundTriggerHw#getProperties()
+ */
+ Properties getProperties();
+
+ /**
+ * @see ISoundTriggerHw#registerGlobalCallback(ISoundTriggerHwGlobalCallback)
+ */
+ void registerCallback(GlobalCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#loadSoundModel(android.media.soundtrigger.SoundModel,
+ * ISoundTriggerHwCallback)
+ */
+ int loadSoundModel(SoundModel soundModel, ModelCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#loadPhraseSoundModel(android.media.soundtrigger.PhraseSoundModel,
+ * ISoundTriggerHwCallback)
+ */
+ int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#unloadSoundModel(int)
+ */
+ void unloadSoundModel(int modelHandle);
+
+ /**
+ * @see ISoundTriggerHw#startRecognition(int, int, int, RecognitionConfig)
+ */
+ void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config);
+
+ /**
+ * @see ISoundTriggerHw#stopRecognition(int)
+ */
+ void stopRecognition(int modelHandle);
+
+ /**
+ * @see ISoundTriggerHw#forceRecognitionEvent(int)
+ */
+ void forceRecognitionEvent(int modelHandle);
+
+ /**
+ * @return null if not supported.
+ * @see ISoundTriggerHw#queryParameter(int, int)
+ */
+ ModelParameterRange queryParameter(int modelHandle, int param);
+
+ /**
+ * @see ISoundTriggerHw#getParameter(int, int)
+ */
+ int getModelParameter(int modelHandle, int param);
+
+ /**
+ * @see ISoundTriggerHw#setParameter(int, int, int)
+ */
+ void setModelParameter(int modelHandle, int param, int value);
+
+ /**
+ * @see IBinder#getInterfaceDescriptor()
+ */
+ String interfaceDescriptor();
+
+ /**
+ * @see IBinder#linkToDeath(IBinder.DeathRecipient, int)
+ */
+ void linkToDeath(IBinder.DeathRecipient recipient);
+
+ /**
+ * @see IBinder#unlinkToDeath(IBinder.DeathRecipient, int)
+ */
+ void unlinkToDeath(IBinder.DeathRecipient recipient);
+
+ /*
+ * This is only useful for testing decorators and doesn't actually do anything with the real
+ * HAL. This method would block until all callbacks that were previously generated have been
+ * invoked. For most decorators, this merely flushes the delegate, but for delegates that may
+ * have additional buffers for callbacks this should flush them.
+ */
+ void flushCallbacks();
+
+ /**
+ * Kill and restart the HAL instance. This is typically a last resort for error recovery and may
+ * result in other related services being killed.
+ */
+ void reboot();
+
+ /**
+ * Called when this interface is guaranteed to no longer be used and can free up any resources
+ * used.
+ */
+ void detach();
+
+ /**
+ * Callback interface for model-related events.
+ */
+ interface ModelCallback {
+ /**
+ * @see ISoundTriggerHwCallback#recognitionCallback(int, RecognitionEvent)
+ */
+ void recognitionCallback(int modelHandle, RecognitionEvent event);
+
+ /**
+ * @see ISoundTriggerHwCallback#phraseRecognitionCallback(int, PhraseRecognitionEvent)
+ */
+ void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event);
+
+ /**
+ * @see ISoundTriggerHwCallback#modelUnloaded(int)
+ */
+ void modelUnloaded(int modelHandle);
+ }
+
+ /**
+ * Callback interface for global events.
+ */
+ interface GlobalCallback {
+ /**
+ * @see ISoundTriggerHwGlobalCallback#onResourcesAvailable()
+ */
+ void onResourcesAvailable();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
deleted file mode 100644
index 8b434bd84363..000000000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hidl.base.V1_0.IBase;
-import android.os.IHwBinder;
-
-/**
- * This interface mimics android.hardware.soundtrigger.V2_x.ISoundTriggerHw and
- * android.hardware.soundtrigger.V2_x.ISoundTriggerHwCallback, with a few key differences:
- * <ul>
- * <li>Methods in the original interface generally have a status return value and potentially a
- * second return value which is the actual return value. This is reflected via a synchronous
- * callback, which is not very pleasant to work with. This interface replaces that pattern with
- * the convention that a HalException is thrown for non-OK status, and then we can use the
- * return value for the actual return value.
- * <li>This interface will always include all the methods from the latest 2.x version (and thus
- * from every 2.x version) interface, with the convention that unsupported methods throw a
- * {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
- * code.
- * <li>Cases where the original interface had multiple versions of a method representing the exact
- * thing, or there exists a trivial conversion between the new and old version, this interface
- * represents only the latest version, without any _version suffixes.
- * <li>Removes some of the obscure IBinder methods.
- * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
- * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
- * them.
- * <li>soundModelCallback has been removed, since nobody cares about it. Implementations are free
- * to silently discard it.
- * </ul>
- * For cases where the client wants to explicitly handle specific versions of the underlying driver
- * interface, they may call {@link #interfaceDescriptor()}.
- * <p>
- * <b>Note to maintainers</b>: This class must always be kept in sync with the latest 2.x version,
- * so that clients have access to the entire functionality without having to burden themselves with
- * compatibility, as much as possible.
- */
-public interface ISoundTriggerHw2 {
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getPropertiesEx(
- * android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getPropertiesExCallback)
- */
- android.hardware.soundtrigger.V2_3.Properties getProperties();
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback)
- */
- int loadSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadPhraseSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback)
- */
- int loadPhraseSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#unloadSoundModel(int)
- */
- void unloadSoundModel(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopRecognition(int)
- */
- void stopRecognition(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopAllRecognitions()
- */
- void stopAllRecognitions();
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#startRecognition_2_3(int,
- * android.hardware.soundtrigger.V2_3.RecognitionConfig,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int)
- */
- void startRecognition(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getModelState(int)
- */
- void getModelState(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getParameter(int, int,
- * ISoundTriggerHw.getParameterCallback)
- */
- int getModelParameter(int modelHandle, int param);
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#setParameter(int, int, int)
- */
- void setModelParameter(int modelHandle, int param, int value);
-
- /**
- * @return null if not supported.
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#queryParameter(int, int,
- * ISoundTriggerHw.queryParameterCallback)
- */
- ModelParameterRange queryParameter(int modelHandle, int param);
-
- /**
- * @see IHwBinder#linkToDeath(IHwBinder.DeathRecipient, long)
- */
- boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie);
-
- /**
- * @see IHwBinder#unlinkToDeath(IHwBinder.DeathRecipient)
- */
- boolean unlinkToDeath(IHwBinder.DeathRecipient recipient);
-
- /**
- * @see IBase#interfaceDescriptor()
- */
- String interfaceDescriptor() throws android.os.RemoteException;
-
- interface Callback {
- /**
- * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#recognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent,
- * int)
- */
- void recognitionCallback(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
- int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#phraseRecognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent,
- * int)
- */
- void phraseRecognitionCallback(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
- int cookie);
- }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
index a90053a23dea..60f89da8e4d3 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
@@ -16,18 +16,18 @@
package com.android.server.soundtrigger_middleware;
-import android.media.ICaptureStateListener;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
/**
- * This interface unifies methods from ISoundTriggerMiddlewareService and ICaptureStateListener.
+ * This interface closely follows ISoundTriggerMiddlewareService with some subtle changes for
+ * convenience.
*
* The ISoundTriggerMiddlewareService have been modified to exclude identity information and the
* RemoteException signature, both of which are only relevant at the service boundary layer.
*/
-public interface ISoundTriggerMiddlewareInternal extends ICaptureStateListener {
+public interface ISoundTriggerMiddlewareInternal {
/**
* Query the available modules and their capabilities.
*/
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/core/java/com/android/server/soundtrigger_middleware/README.md
index 416548d9bc5e..016e5c95a851 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/README.md
+++ b/services/core/java/com/android/server/soundtrigger_middleware/README.md
@@ -1,19 +1,145 @@
# Sound Trigger Middleware
-TODO: Add component description.
-## Notes about thread synchronization
+## Overview
+Sound Trigger Middleware is a system service that exposes sound trigger functionality (low-power
+detection of acoustic events) to applications and higher-level system service.
+
+It has the following roles:
+- Isolating the soundtrigger HAL from potentially untrusted clients.
+- Enforcing correct behavior of the clients.
+- Enforcing correct behavior of the HAL and attempting to recover from failures.
+- Enforcing permissions for using soundtrigger functionality.
+- Serializing access to the HAL.
+- Logging soundtrigger usage in a comprehensive and consistent manner.
+- Generating a dumpsys report including current state and history of operations.
+- Providing a standard interface regardless which version of the HAL is implemented and gracefully
+ degrading operation whenever necessary.
+
+## Structure
+
+The service implementation can be divided into three main layers:
+
+- The "bottom layer" is concerned with HAL compatibility - making all HAL versions look and behave
+ the same.
+- The "middle layer" is concerned with the business logic of the service.
+- The "top layer" is concerned with exposing this functionality as a System Service and integrating
+ with other parts of the system.
+
+### HAL Compatibility Layer
+
+This layer implements the `ISoundTriggerHal` interface, which is the version-agnostic representation
+of the sound trigger HAL driver. It has two main implementations, `SoundTriggerHw2Compat` and
+`SoundTriggerHw3Compat` responsible for adapting to V2.x and V3 HAL drivers, respectively, including
+supporting their respective minor-version differences.
+
+This layer also includes several `ISoundTriggerHal` decorators, such as `SoundTriggerHalWatchdog`
+that enforces deadlines on calls into the HAL, and `SoundTriggerHalEnforcer` which enforces that
+the HAL respects the expected protocol.
+
+The decorator-based design is an effective tool for separation of aspects and modularity, thus
+keeping classes relatively small and focused on one concern. It is also very effective for
+testability by following dependency injection principles.
+
+### Business Logic Layer
+
+This layer also uses a decorator-based design for separation of concerns. The main interface being
+decorated is `ISoundTriggerMiddlwareInternal`, which closely follows the external-facing AIDL
+interface, `ISoundTriggerMiddlewareService`.
+
+Each of the decorators serves a focused purpose: for example, `SoundTriggerMiddlwarePermission`
+deals with enforcing permissions required for the various methods, `SoundTriggerMiddlewareLogging`
+logs all API usage, `SoundTriggerMiddlewareValidation` enforces correct usage of the protocol and
+isolates client errors from internal server errors.
+
+At the bottom of this decorator stack is `SoundTriggerMiddlewareImpl` / `SoundTriggerModule`, which
+are the adapter between `ISoundTriggerHal` and `ISoundTriggerMiddlwareInternal`, introducing the
+notion of having separate client sessions sharing the same HAL.
+
+### Service Layer
+
+This layer ties everything together. It instantiates the actual system service and the decorator
+stack. It also provides concrete connections to the Audio service (for negotiating sessions shared
+between Audio and Sound Trigger and for notifications about audio recording) and to the various HAL
+factories.
+
+This is the only layer that makes strong assumptions about the environment instead of relying on
+abstractions.
+
+## Error Handling and Exception Conventions
+
+We follow conventions for usage of exceptions in the service, in order to correctly and consistently
+distinguish the following cases:
+
+1. The client has done something wrong.
+2. The service implementation has done something wrong.
+3. The HAL has done something wrong.
+4. Nobody has done anything wrong, but runtime conditions prevent an operation from being fulfilled
+ as intended.
+
+The `SoundTriggerMiddlewarePermission` class would reject any calls from unauthorized clients,
+responding with the appropriate exception.
+
+The `SoundTriggerMiddlewareValidation` class does much of this separation. By validating the
+client's data and state, it would throw a relevant `RuntimeException` exception to the client
+without passing the requests down to the lower layers. Once that is done, any exception thrown from
+the underlying implementation can be assumed to be not the client's fault. If caught, they will be
+classified according to the following rule:
+
+- If they are `RecoverableException`s, they represent category #4 above, and will be presented to
+ the client as `ServiceSpecificException`s with the same error code.
+- Otherwise, they are considered an internal error (including HAL malfunction) and will be
+ presented to the client as `ServiceSpecificException(Status.INTERNAL_ERROR)`.
+
+Internally, we would throw `RecoverableException` whenever appropriate. Whenever a HAL malfunctions,
+`SoundTriggerHalEnforcer` is responsible for rebooting it and throwing an exception. A HAL death is
+considered a valid failure mode, and thus result in `RecoverableException(Status.DEAD_OBJECT)`,
+which ends up as a `ServiceSpecificException(Status.DEAD_OBJECT)` on the client side.
+
+## Notes About Thread Synchronization
This component has some tricky thread synchronization considerations due to its layered design and
due to the fact that it is involved in both in-bound and out-bound calls from / to
-external components. To avoid potential deadlocks, a strict locking order must be ensured whenever
-nesting locks. The order is:
-- `SoundTriggerMiddlewareValidation` lock.
-- Audio policy service lock. This one is external - it should be assumed to be held whenever we're
+external components.
+
+The following mutexes need to be considered:
+- Typically, a one or more mutexes that exist in every layer of the sound trigger middleware stack
+ to serialize access to its internal state or to external components.
+- Audio Policy Service lock. This one is external - it should be assumed to be held whenever we're
inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
- within our calls into `AudioSessionProvider.acquireSession()`.
-- `SoundTriggerModule` lock.
-
-This dictates careful consideration of callbacks going from `SoundTriggerModule` to
-`SoundTriggerMiddlewareValidation` and especially those coming from the `setCaptureState()` path.
-We always invoke those calls outside of the `SoundTriggerModule` lock, so we can lock
-`SoundTriggerMiddlewareValidation`. However, in the `setCaptureState()` case, we have to use atomics
-in `SoundTriggerMiddlewareValidation` and avoid the lock.
+ within our calls into `AudioSessionProvider.acquireSession()` /
+ `AudioSessionProvider.releaseSession()`.
+
+To avoid potential deadlocks, a strict locking order must be ensured whenever nesting locks. The
+order is:
+- Upper layers of the stack, starting from the top (i.e. may not attempt to acquire a higher-layer
+ mutex while a lower-layer mutex is being held) until `ISoundTriggerHw2`.
+- Audio Policy Service lock.
+- Lower layers of the stack, starting from `ISoundTriggerHw2` all the way down to the HAL.
+
+In order to enforce this order, some conventions are established around when it is safe for a module
+to call another module, while having its local mutex(es) held:
+- Most calls (see exceptions below) originating from SoundTriggerMiddlewareService simply propagate
+ down the decorator stack. It is legal to call into the next layer down while holding a local
+ mutex. It is illegal to invoke a callback with a local mutex held.
+- Callbacks propagate from the lower layers up to the upper layers. It is legal to hold a local
+ mutex within a callback, but **not** while call to an upper layer.
+- In order to be able to synchronize, despite the asynchronous nature of callbacks,
+ `stopRecognition()` and `unloadModel()` work differently. They guarantee that once they return,
+ the callbacks associated with them will no longer be called. This implies that they have to block
+ until any pending callbacks are done processing and since these callbacks are potentially holding
+ locks of higher-order mutexes, we must not be holding a local mutex while calling down. The proper
+ sequence for these calls is:
+ - Obtain the local lock if needed. Update/check local state as necessary.
+ - Call the respective method of the delegate ("downwards"). Once it returns, not more callbacks
+ related to this operation will be called.
+ - Obtain the local lock if needed. Update local state as necessary. Assume that state might have
+ changed while the lock has been released.
+ - Release the local lock.
+ - Invoke any synchronous callbacks if needed.
+- Calling from `SoundTriggerMiddlewareImpl` / `SoundTriggerModule` into the audio policy service via
+ `acquireSession()` / `releaseSession()` while holding the local lock is legal.
+- `setCaptureState()` calls, originating from Audio Policy Service, into the lower layers of the
+ stack may call into the HAL (specifically, they must invoke `stopRecognition()`, but must not
+ block on callbacks. For this reason, `SoundTriggerHw2ConcurrentCaptureHandler`, which is the
+ recipient of these calls, features a buffer and an additional thread, which allows the actual
+ stopping to be synchronous, as required, without having to block the call upon higher layers
+ processing the callbacks.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
new file mode 100644
index 000000000000..e3ce71962794
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
@@ -0,0 +1,456 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of concurrent capture
+ * constraints, for HAL implementations older than V2.4 (later versions support this feature at the
+ * HAL level).
+ * <p>
+ * Decorating an instance with this class would result in all active recognitions being aborted as
+ * soon as capture state becomes active. This class ensures consistent handling of abortions coming
+ * from that HAL and abortions coming from concurrent capture, in that only one abort event will be
+ * delivered, irrespective of the relative timing of the two events.
+ * <p>
+ * There are some delicate thread-safety issues handled here:
+ * <ul>
+ * <li>When a model is stopped via stopRecognition(), we guarantee that by the time the call
+ * returns, there will be no more recognition events (including abort) delivered for this model.
+ * This implies synchronous stopping and blocking until all pending events have been delivered.
+ * <li>When a model is stopped via onCaptureStateChange(true), the stopping of the recognition at
+ * the HAL level must be synchronous, but the call must not block on the delivery of the
+ * callbacks, due to the risk of a deadlock: the onCaptureStateChange() calls are typically
+ * invoked with the audio policy mutex held, so must not call method which may attempt to lock
+ * higher-level mutexes. See README.md in this directory for further details.
+ * </ul>
+ * The way this behavior is achieved is by having an additional thread with an event queue, which
+ * joins together model events coming from the delegate module with abort events originating from
+ * this layer (as result of external capture).
+ */
+public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal,
+ ICaptureStateNotifier.Listener {
+ private final @NonNull ISoundTriggerHal mDelegate;
+ private GlobalCallback mGlobalCallback;
+
+ /**
+ * Information about a model that is currently loaded. This is needed in order to be able to
+ * send abort events to its designated callback.
+ */
+ private static class LoadedModel {
+ final int type;
+ final @NonNull ModelCallback callback;
+
+ private LoadedModel(int type, @NonNull ModelCallback callback) {
+ this.type = type;
+ this.callback = callback;
+ }
+ }
+
+ /**
+ * This map holds the model type for every model that is loaded.
+ */
+ private final @NonNull Map<Integer, LoadedModel> mLoadedModels = new ConcurrentHashMap<>();
+
+ /**
+ * A set of all models that are currently active.
+ * We use this in order to know which models to stop in case of external capture.
+ * Used as a lock to synchronize operations that effect activity.
+ */
+ private final @NonNull Set<Integer> mActiveModels = new HashSet<>();
+
+ /**
+ * Notifier for changes in capture state.
+ */
+ private final @NonNull ICaptureStateNotifier mNotifier;
+
+ /**
+ * Whether capture is active.
+ */
+ private boolean mCaptureState;
+
+ /**
+ * Since we're wrapping the death recipient, we need to keep a translation map for unlinking.
+ * Key is the client recipient, value is the wrapper.
+ */
+ private final @NonNull Map<IBinder.DeathRecipient, IBinder.DeathRecipient>
+ mDeathRecipientMap = new ConcurrentHashMap<>();
+
+ private final @NonNull CallbackThread mCallbackThread = new CallbackThread();
+
+ public SoundTriggerHalConcurrentCaptureHandler(
+ @NonNull ISoundTriggerHal delegate,
+ @NonNull ICaptureStateNotifier notifier) {
+ mDelegate = delegate;
+ mNotifier = notifier;
+ mCaptureState = mNotifier.registerListener(this);
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ synchronized (mActiveModels) {
+ if (mCaptureState) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ mActiveModels.add(modelHandle);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ synchronized (mActiveModels) {
+ mDelegate.stopRecognition(modelHandle);
+ mActiveModels.remove(modelHandle);
+ }
+ // Block until all previous events are delivered. Since this is potentially blocking on
+ // upward calls, it must be done outside the lock.
+ mCallbackThread.flush();
+ }
+
+ @Override
+ public void onCaptureStateChange(boolean active) {
+ synchronized (mActiveModels) {
+ if (active) {
+ // Abort all active models. This must be done as one transaction to the event
+ // thread, in order to be able to dedupe events before they are delivered.
+ try (SafeCloseable ignored = mCallbackThread.stallReader()) {
+ for (int modelHandle : mActiveModels) {
+ mDelegate.stopRecognition(modelHandle);
+ LoadedModel model = mLoadedModels.get(modelHandle);
+ // An abort event must be the last one for its model.
+ mCallbackThread.pushWithDedupe(modelHandle, true,
+ () -> notifyAbort(modelHandle, model));
+ }
+ }
+ } else {
+ mGlobalCallback.onResourcesAvailable();
+ }
+
+ mCaptureState = active;
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ int handle = mDelegate.loadSoundModel(soundModel, new CallbackWrapper(callback));
+ mLoadedModels.put(handle, new LoadedModel(SoundModelType.GENERIC, callback));
+ return handle;
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ int handle = mDelegate.loadPhraseSoundModel(soundModel, new CallbackWrapper(callback));
+ mLoadedModels.put(handle, new LoadedModel(SoundModelType.KEYPHRASE, callback));
+ return handle;
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ mLoadedModels.remove(modelHandle);
+ mDelegate.unloadSoundModel(modelHandle);
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = new GlobalCallback() {
+ @Override
+ public void onResourcesAvailable() {
+ mCallbackThread.push(callback::onResourcesAvailable);
+ }
+ };
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ IBinder.DeathRecipient wrapper = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallbackThread.push(() -> recipient.binderDied());
+ }
+ };
+ mDelegate.linkToDeath(wrapper);
+ mDeathRecipientMap.put(recipient, wrapper);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(mDeathRecipientMap.remove(recipient));
+ }
+
+ private class CallbackWrapper implements ISoundTriggerHal.ModelCallback {
+ private final @NonNull ISoundTriggerHal.ModelCallback mDelegateCallback;
+
+ private CallbackWrapper(@NonNull ModelCallback delegateCallback) {
+ mDelegateCallback = delegateCallback;
+ }
+
+ @Override
+ public void recognitionCallback(int modelHandle, RecognitionEvent event) {
+ // A recognition event must be the last one for its model, unless it is a forced one
+ // (those leave the model active).
+ mCallbackThread.pushWithDedupe(modelHandle,
+ event.status != RecognitionStatus.FORCED,
+ () -> mDelegateCallback.recognitionCallback(modelHandle, event));
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event) {
+ // A recognition event must be the last one for its model, unless it is a forced one
+ // (those leave the model active).
+ mCallbackThread.pushWithDedupe(modelHandle,
+ event.common.status != RecognitionStatus.FORCED,
+ () -> mDelegateCallback.phraseRecognitionCallback(modelHandle, event));
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ mCallbackThread.push(() -> mDelegateCallback.modelUnloaded(modelHandle));
+ }
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ mCallbackThread.flush();
+ }
+
+ /**
+ * This is a thread for asynchronous delivery of callback events, having the following features:
+ * <ul>
+ * <li>Events are processed on a separate thread than the thread that pushed them, in the order
+ * they were pushed.
+ * <li>Events can be deduped upon entry to the queue. This is achieved as follows:
+ * <ul>
+ * <li>Temporarily stall the reader via {@link #stallReader()}.
+ * <li>Within this scope, push as many events as needed via
+ * {@link #pushWithDedupe(int, boolean, Runnable)}.
+ * If an event with the same model handle as the one being pushed is already in the queue
+ * and has been marked as "lastForModel", the new event will be discarded before entering
+ * the queue.
+ * <li>Finally, un-stall the reader by existing the scope.
+ * <li>Events that do not require deduping can be pushed via {@link #push(Runnable)}.
+ * </ul>
+ * <li>Events can be flushed via {@link #flush()}. This will block until all events pushed prior
+ * to this call have been fully processed.
+ * </ul>
+ */
+ private static class CallbackThread {
+ private static class Entry {
+ final boolean lastForModel;
+ final int modelHandle;
+ final Runnable runnable;
+
+ private Entry(boolean lastForModel, int modelHandle, Runnable runnable) {
+ this.lastForModel = lastForModel;
+ this.modelHandle = modelHandle;
+ this.runnable = runnable;
+ }
+ }
+
+ private boolean mStallReader = false;
+ private final Queue<Entry> mList = new LinkedList<>();
+ private int mPushCount = 0;
+ private int mProcessedCount = 0;
+
+ /**
+ * Ctor. Starts the thread.
+ */
+ CallbackThread() {
+ new Thread(() -> {
+ try {
+ while (true) {
+ pop().run();
+ synchronized (mList) {
+ mProcessedCount++;
+ mList.notifyAll();
+ }
+ }
+ } catch (InterruptedException e) {
+ // If interrupted, exit.
+ }
+ }).start();
+ }
+
+ /**
+ * Push a new runnable to the queue, with no deduping.
+ *
+ * @param runnable The runnable to push.
+ */
+ void push(Runnable runnable) {
+ pushEntry(new Entry(false, 0, runnable), false);
+ }
+
+
+ /**
+ * Push a new runnable to the queue, with deduping.
+ * If an entry with the same model handle is already in the queue and was designated as
+ * last for model, this one will be discarded.
+ *
+ * @param modelHandle The model handle, used for deduping purposes.
+ * @param lastForModel If true, this entry will be considered the last one for this model
+ * and any subsequence calls for this handle (whether lastForModel or
+ * not) will be discarded while this entry is in the queue.
+ * @param runnable The runnable to push.
+ */
+ void pushWithDedupe(int modelHandle, boolean lastForModel, Runnable runnable) {
+ pushEntry(new Entry(lastForModel, modelHandle, runnable), true);
+ }
+
+ /**
+ * Block until every entry pushed prior to this call has been processed.
+ */
+ void flush() {
+ try {
+ synchronized (mList) {
+ int pushCount = mPushCount;
+ while (mProcessedCount != pushCount) {
+ mList.wait();
+ }
+ }
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ /**
+ * Creates a scope (using a try-with-resources block), within which events that are pushed
+ * remain queued and processed. This is useful in order to utilize deduping.
+ */
+ SafeCloseable stallReader() {
+ synchronized (mList) {
+ mStallReader = true;
+ return () -> {
+ synchronized (mList) {
+ mStallReader = false;
+ mList.notifyAll();
+ }
+ };
+ }
+ }
+
+ private void pushEntry(Entry entry, boolean dedupe) {
+ synchronized (mList) {
+ if (dedupe) {
+ for (Entry existing : mList) {
+ if (existing.lastForModel && existing.modelHandle == entry.modelHandle) {
+ return;
+ }
+ }
+ }
+ mList.add(entry);
+ mPushCount++;
+ mList.notifyAll();
+ }
+ }
+
+ private Runnable pop() throws InterruptedException {
+ synchronized (mList) {
+ while (mStallReader || mList.isEmpty()) {
+ mList.wait();
+ }
+ return mList.remove().runnable;
+ }
+ }
+ }
+
+ /** Notify the client that recognition has been aborted. */
+ private static void notifyAbort(int modelHandle, LoadedModel model) {
+ switch (model.type) {
+ case SoundModelType.GENERIC: {
+ RecognitionEvent event = new RecognitionEvent();
+ event.status = RecognitionStatus.ABORTED;
+ event.type = SoundModelType.GENERIC;
+ model.callback.recognitionCallback(modelHandle, event);
+ }
+ break;
+
+ case SoundModelType.KEYPHRASE: {
+ PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+ event.common.status = RecognitionStatus.ABORTED;
+ event.common.type = SoundModelType.KEYPHRASE;
+ model.callback.phraseRecognitionCallback(modelHandle, event);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ mNotifier.unregisterListener(this);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // All methods below do trivial delegation - no interesting logic.
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
new file mode 100644
index 000000000000..6870f4feb3de
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
+ * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
+ * common HAL malfunctions, to help track them and assist in debugging.
+ *
+ * The class is thread-safe.
+ */
+public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
+ private static final String TAG = "SoundTriggerHalEnforcer";
+
+ /** The state of a model. */
+ private enum ModelState {
+ /** Model is loaded, but inactive. */
+ INACTIVE,
+ /** Model is active. */
+ ACTIVE,
+ /** A request to stop is being made, which may or may not have been processed yet. */
+ PENDING_STOP,
+ }
+
+ private final ISoundTriggerHal mUnderlying;
+ private final Map<Integer, ModelState> mModelStates = new HashMap<>();
+
+ public SoundTriggerHalEnforcer(
+ ISoundTriggerHal underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public Properties getProperties() {
+ try {
+ return mUnderlying.getProperties();
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ mUnderlying.registerCallback(callback);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ try {
+ synchronized (mModelStates) {
+ int handle = mUnderlying.loadSoundModel(soundModel,
+ new ModelCallbackEnforcer(callback));
+ mModelStates.put(handle, ModelState.INACTIVE);
+ return handle;
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ try {
+ synchronized (mModelStates) {
+ int handle = mUnderlying.loadPhraseSoundModel(soundModel,
+ new ModelCallbackEnforcer(callback));
+ mModelStates.put(handle, ModelState.INACTIVE);
+ return handle;
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try {
+ // This call into the HAL may block on callback processing, thus must be done outside
+ // of the critical section. After this call returns we are guaranteed to no longer be
+ // getting unload events for that model.
+ mUnderlying.unloadSoundModel(modelHandle);
+ synchronized (mModelStates) {
+ // At this point, the model may have already been removed by a HAL callback, but the
+ // remove() method is a no-op in this case, so thus safe.
+ mModelStates.remove(modelHandle);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try {
+ // This call into the HAL may block on callback processing, thus must be done outside
+ // of the critical section. After this call returns we are guaranteed to no longer be
+ // getting stop events for that model.
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, ModelState.PENDING_STOP);
+ }
+ mUnderlying.stopRecognition(modelHandle);
+ synchronized (mModelStates) {
+ // At this point, the model might have been preemptively unloaded, but replace()
+ // do nothing when the entry does not exist, so all good.
+ mModelStates.replace(modelHandle, ModelState.INACTIVE);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ try {
+ synchronized (mModelStates) {
+ mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ mModelStates.replace(modelHandle, ModelState.ACTIVE);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ try {
+ mUnderlying.forceRecognitionEvent(modelHandle);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try {
+ return mUnderlying.getModelParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try {
+ mUnderlying.setModelParameter(modelHandle, param, value);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try {
+ return mUnderlying.queryParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mUnderlying.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mUnderlying.flushCallbacks();
+ }
+
+ private RuntimeException handleException(RuntimeException e) {
+ if (e instanceof RecoverableException) {
+ throw e;
+ }
+ if (e.getCause() instanceof DeadObjectException) {
+ // Server is dead, no need to reboot.
+ Log.e(TAG, "HAL died");
+ throw new RecoverableException(Status.DEAD_OBJECT);
+ }
+ Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ reboot();
+ throw e;
+ }
+
+ @Override
+ public void reboot() {
+ mUnderlying.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mUnderlying.detach();
+ }
+
+ private class ModelCallbackEnforcer implements ModelCallback {
+ private final ModelCallback mUnderlying;
+
+ private ModelCallbackEnforcer(
+ ModelCallback underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public void recognitionCallback(int model, RecognitionEvent event) {
+ int status = event.status;
+
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(model);
+ if (state == null || state == ModelState.INACTIVE) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ reboot();
+ return;
+ }
+ if (status != RecognitionStatus.FORCED) {
+ mModelStates.replace(model, ModelState.INACTIVE);
+ }
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.recognitionCallback(model, event);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+ int status = event.common.status;
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(model);
+ if (state == null || state == ModelState.INACTIVE) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ reboot();
+ return;
+ }
+ if (status != RecognitionStatus.FORCED) {
+ mModelStates.replace(model, ModelState.INACTIVE);
+ }
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.phraseRecognitionCallback(model, event);
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(modelHandle);
+ if (state == null) {
+ Log.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
+ reboot();
+ return;
+ }
+
+ if (state == ModelState.ACTIVE) {
+ Log.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
+ reboot();
+ return;
+ }
+ mModelStates.remove(modelHandle);
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.modelUnloaded(modelHandle);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
new file mode 100644
index 000000000000..7dd28e0b88f9
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.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 com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of the maximum number
+ * of models supported by the HAL, for HAL implementations older than V2.4 that do not support
+ * rejection of model loading at the HAL layer.
+ * Since preemptive model unloading has been introduced in V2.4, it should never be used in
+ * conjunction with this class, hence we don't bother considering preemtive unloading when counting
+ * the number of currently loaded models.
+ */
+public class SoundTriggerHalMaxModelLimiter implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHal mDelegate;
+ private final int mMaxModels;
+
+ // This counter is used to enforce the maximum number of loaded models.
+ private int mNumLoadedModels = 0;
+
+ private GlobalCallback mGlobalCallback;
+
+ public SoundTriggerHalMaxModelLimiter(
+ ISoundTriggerHal delegate, int maxModels) {
+ mDelegate = delegate;
+ this.mMaxModels = maxModels;
+ }
+
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = callback;
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ synchronized (this) {
+ if (mNumLoadedModels == mMaxModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ int result = mDelegate.loadSoundModel(soundModel, callback);
+ ++mNumLoadedModels;
+ return result;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ synchronized (this) {
+ if (mNumLoadedModels == mMaxModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ int result = mDelegate.loadPhraseSoundModel(soundModel, callback);
+ ++mNumLoadedModels;
+ return result;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ boolean wasAtMaxCapacity;
+ synchronized (this) {
+ wasAtMaxCapacity = mNumLoadedModels-- == mMaxModels;
+ }
+ try {
+ mDelegate.unloadSoundModel(modelHandle);
+ } catch (Exception e) {
+ synchronized (this) {
+ ++mNumLoadedModels;
+ }
+ throw e;
+ }
+ if (wasAtMaxCapacity) {
+ // It is legal to invoke callbacks from within unloadSoundModel().
+ // See README.md for details.
+ mGlobalCallback.onResourcesAvailable();
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 212f81f72b24..5fe06eef146d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -17,13 +17,12 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
import android.util.Log;
import java.util.Objects;
@@ -31,21 +30,19 @@ import java.util.Timer;
import java.util.TimerTask;
/**
- * An {@link ISoundTriggerHw2} decorator that would enforce deadlines on all calls and reboot the
+ * An {@link ISoundTriggerHal} decorator that would enforce deadlines on all calls and reboot the
* HAL whenever they expire.
*/
-public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
+public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
private static final long TIMEOUT_MS = 3000;
- private static final String TAG = "SoundTriggerHw2Watchdog";
+ private static final String TAG = "SoundTriggerHalWatchdog";
- private final @NonNull
- ISoundTriggerHw2 mUnderlying;
- private final @NonNull
- Timer mTimer;
+ private final @NonNull ISoundTriggerHal mUnderlying;
+ private final @NonNull Timer mTimer;
- public SoundTriggerHw2Watchdog(@NonNull ISoundTriggerHw2 underlying) {
+ public SoundTriggerHalWatchdog(@NonNull ISoundTriggerHal underlying) {
mUnderlying = Objects.requireNonNull(underlying);
- mTimer = new Timer("SoundTriggerHw2Watchdog");
+ mTimer = new Timer("SoundTriggerHalWatchdog");
}
@Override
@@ -56,54 +53,53 @@ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
}
@Override
- public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
- int cookie) {
+ public void registerCallback(GlobalCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- return mUnderlying.loadSoundModel(soundModel, callback, cookie);
+ mUnderlying.registerCallback(callback);
}
}
@Override
- public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
- int cookie) {
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- return mUnderlying.loadPhraseSoundModel(soundModel, callback, cookie);
+ return mUnderlying.loadSoundModel(soundModel, callback);
}
}
@Override
- public void unloadSoundModel(int modelHandle) {
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.unloadSoundModel(modelHandle);
+ return mUnderlying.loadPhraseSoundModel(soundModel, callback);
}
}
@Override
- public void stopRecognition(int modelHandle) {
+ public void unloadSoundModel(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.stopRecognition(modelHandle);
+ mUnderlying.unloadSoundModel(modelHandle);
}
}
@Override
- public void stopAllRecognitions() {
+ public void stopRecognition(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.stopAllRecognitions();
+ mUnderlying.stopRecognition(modelHandle);
}
}
@Override
- public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
- int cookie) {
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.startRecognition(modelHandle, config, callback, cookie);
+ mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
}
}
@Override
- public void getModelState(int modelHandle) {
+ public void forceRecognitionEvent(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.getModelState(modelHandle);
+ mUnderlying.forceRecognitionEvent(modelHandle);
}
}
@@ -129,23 +125,33 @@ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
}
@Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mUnderlying.linkToDeath(recipient, cookie);
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.linkToDeath(recipient);
}
@Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mUnderlying.unlinkToDeath(recipient);
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.unlinkToDeath(recipient);
}
@Override
- public String interfaceDescriptor() throws RemoteException {
+ public String interfaceDescriptor() {
return mUnderlying.interfaceDescriptor();
}
- private static void rebootHal() {
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
+ @Override
+ public void flushCallbacks() {
+ mUnderlying.flushCallbacks();
+ }
+
+ @Override
+ public void reboot() {
+ mUnderlying.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mUnderlying.detach();
}
private class Watchdog implements AutoCloseable {
@@ -160,7 +166,7 @@ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
@Override
public void run() {
Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
- rebootHal();
+ reboot();
}
};
mTimer.schedule(mTask, TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 2f087f46da86..7a1f775d6036 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -18,60 +18,116 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.Status;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
import android.os.IHwBinder;
import android.os.RemoteException;
+import android.system.OsConstants;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
- * An implementation of {@link ISoundTriggerHw2}, on top of any
+ * An implementation of {@link ISoundTriggerHal}, on top of any
* android.hardware.soundtrigger.V2_x.ISoundTriggerHw implementation. This class hides away some of
* the details involved with retaining backward compatibility and adapts to the more pleasant syntax
- * exposed by {@link ISoundTriggerHw2}, compared to the bare driver interface.
+ * exposed by {@link ISoundTriggerHal}, compared to the bare driver interface.
* <p>
* Exception handling:
* <ul>
* <li>All {@link RemoteException}s get rethrown as {@link RuntimeException}.
* <li>All HAL malfunctions get thrown as {@link HalException}.
* <li>All unsupported operations get thrown as {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED}
* code.
* </ul>
*/
-final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
- private final @NonNull
- IHwBinder mBinder;
- private final @NonNull
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
- private final @Nullable
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
- private final @Nullable
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
- private final @Nullable
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
-
- public SoundTriggerHw2Compat(
- @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw underlying) {
- this(underlying.asBinder());
+final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+ private final @NonNull Runnable mRebootRunnable;
+ private final @NonNull IHwBinder mBinder;
+ private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
+ private @Nullable android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
+ private @Nullable android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
+ private @Nullable android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+ private @Nullable android.hardware.soundtrigger.V2_4.ISoundTriggerHw mUnderlying_2_4;
+
+ // HAL <=2.1 requires us to pass a callback argument to startRecognition. We will store the one
+ // passed on load and then pass it on start. We don't bother storing the callback on newer
+ // versions.
+ private final @NonNull ConcurrentMap<Integer, ModelCallback> mModelCallbacks =
+ new ConcurrentHashMap<>();
+
+ // A map from IBinder.DeathRecipient to IHwBinder.DeathRecipient for doing the mapping upon
+ // unlinking.
+ private final @NonNull Map<IBinder.DeathRecipient, IHwBinder.DeathRecipient>
+ mDeathRecipientMap = new HashMap<>();
+
+ // The properties are read at construction time and cached, since we need to use some of them
+ // to enforce constraints.
+ private final @NonNull Properties mProperties;
+
+ static ISoundTriggerHal create(
+ @NonNull ISoundTriggerHw underlying,
+ @NonNull Runnable rebootRunnable,
+ ICaptureStateNotifier notifier) {
+ return create(underlying.asBinder(), rebootRunnable, notifier);
}
- public SoundTriggerHw2Compat(IHwBinder binder) {
- Objects.requireNonNull(binder);
+ static ISoundTriggerHal create(@NonNull IHwBinder binder,
+ @NonNull Runnable rebootRunnable,
+ ICaptureStateNotifier notifier) {
+ SoundTriggerHw2Compat compat = new SoundTriggerHw2Compat(binder, rebootRunnable);
+ ISoundTriggerHal result = compat;
+ // Add max model limiter for versions <2.4.
+ if (compat.mUnderlying_2_4 == null) {
+ result = new SoundTriggerHalMaxModelLimiter(result,
+ compat.mProperties.maxSoundModels);
+ }
+ // Add concurrent capture handler for versions <2.4 which do not support concurrent capture.
+ if (compat.mUnderlying_2_4 == null && !compat.mProperties.concurrentCapture) {
+ result = new SoundTriggerHalConcurrentCaptureHandler(result, notifier);
+ }
+ return result;
+ }
- mBinder = binder;
+ private SoundTriggerHw2Compat(@NonNull IHwBinder binder, @NonNull Runnable rebootRunnable) {
+ mRebootRunnable = Objects.requireNonNull(rebootRunnable);
+ mBinder = Objects.requireNonNull(binder);
+ initUnderlying(binder);
+ mProperties = Objects.requireNonNull(getPropertiesInternal());
+ }
+ private void initUnderlying(IHwBinder binder) {
// We want to share the proxy instances rather than create a separate proxy for every
// version, so we go down the versions in descending order to find the latest one supported,
// and then simply up-cast it to obtain all the versions that are earlier.
+ // Attempt 2.4
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4 =
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.asInterface(binder);
+ if (as2_4 != null) {
+ mUnderlying_2_0 =
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = as2_4;
+ return;
+ }
+
// Attempt 2.3
android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
if (as2_3 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
+ mUnderlying_2_4 = null;
return;
}
@@ -80,7 +136,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
if (as2_2 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
- mUnderlying_2_3 = null;
+ mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -89,7 +145,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
if (as2_1 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
- mUnderlying_2_2 = mUnderlying_2_3 = null;
+ mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -98,7 +154,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
if (as2_0 != null) {
mUnderlying_2_0 = as2_0;
- mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -111,12 +167,32 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
}
+ private static void handleHalStatusAllowBusy(int status, String methodName) {
+ if (status == -OsConstants.EBUSY) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ handleHalStatus(status, methodName);
+ }
+
+ @Override
+ public void reboot() {
+ mRebootRunnable.run();
+ }
+
+ @Override
+ public void detach() {
+ // No-op.
+ }
+
@Override
- public android.hardware.soundtrigger.V2_3.Properties getProperties() {
+ public Properties getProperties() {
+ return mProperties;
+ }
+
+ private Properties getPropertiesInternal() {
try {
AtomicInteger retval = new AtomicInteger(-1);
- AtomicReference<android.hardware.soundtrigger.V2_3.Properties>
- properties =
+ AtomicReference<android.hardware.soundtrigger.V2_3.Properties> properties =
new AtomicReference<>();
try {
as2_3().getProperties_2_3(
@@ -129,30 +205,57 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return getProperties_2_0();
}
handleHalStatus(retval.get(), "getProperties_2_3");
- return properties.get();
+ return ConversionUtil.hidl2aidlProperties(properties.get());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public int loadSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- Callback callback, int cookie) {
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ try {
+ as2_4().registerGlobalCallback(new GlobalCallbackWrapper(callback));
+ } catch (NotSupported e) {
+ // In versions < 2.4 the events represented by this callback don't exist, we can
+ // safely ignore this.
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.SoundModel hidlModel =
+ ConversionUtil.aidl2hidlSoundModel(soundModel);
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
+
try {
- as2_1().loadSoundModel_2_1(soundModel, new SoundTriggerCallback(callback), cookie,
+ as2_4().loadSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
(r, h) -> {
retval.set(r);
handle.set(h);
});
+ handleHalStatusAllowBusy(retval.get(), "loadSoundModel_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- return loadSoundModel_2_0(soundModel, callback, cookie);
+ // Fall-back to the 2.1 version:
+ try {
+ as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
+ (r, h) -> {
+ retval.set(r);
+ handle.set(h);
+ });
+ handleHalStatus(retval.get(), "loadSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadSoundModel_2_0(hidlModel, callback);
+ }
}
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -160,24 +263,35 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public int loadPhraseSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- Callback callback, int cookie) {
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
+ ConversionUtil.aidl2hidlPhraseSoundModel(soundModel);
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
try {
- as2_1().loadPhraseSoundModel_2_1(soundModel, new SoundTriggerCallback(callback),
- cookie,
+ as2_4().loadPhraseSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
(r, h) -> {
retval.set(r);
handle.set(h);
});
+ handleHalStatusAllowBusy(retval.get(), "loadPhraseSoundModel_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- return loadPhraseSoundModel_2_0(soundModel, callback, cookie);
+ // Fall-back to the 2.1 version:
+ try {
+ as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
+ (r, h) -> {
+ retval.set(r);
+ handle.set(h);
+ });
+ handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadPhraseSoundModel_2_0(hidlModel, callback);
+ }
}
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -187,6 +301,8 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
@Override
public void unloadSoundModel(int modelHandle) {
try {
+ // Safe if key doesn't exist.
+ mModelCallbacks.remove(modelHandle);
int retval = as2_0().unloadSoundModel(modelHandle);
handleHalStatus(retval, "unloadSoundModel");
} catch (RemoteException e) {
@@ -206,26 +322,23 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public void stopAllRecognitions() {
- try {
- int retval = as2_0().stopAllRecognitions();
- handleHalStatus(retval, "stopAllRecognitions");
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- @Override
- public void startRecognition(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie) {
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
+ ConversionUtil.aidl2hidlRecognitionConfig(config, deviceHandle, ioHandle);
try {
try {
- int retval = as2_3().startRecognition_2_3(modelHandle, config);
- handleHalStatus(retval, "startRecognition_2_3");
+ int retval = as2_4().startRecognition_2_4(modelHandle, hidlConfig);
+ handleHalStatusAllowBusy(retval, "startRecognition_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- startRecognition_2_1(modelHandle, config, callback, cookie);
+ // Fall-back to the 2.3 version:
+ try {
+ int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
+ handleHalStatus(retval, "startRecognition_2_3");
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ startRecognition_2_1(modelHandle, hidlConfig);
+ }
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -233,7 +346,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public void getModelState(int modelHandle) {
+ public void forceRecognitionEvent(int modelHandle) {
try {
int retval = as2_2().getModelState(modelHandle);
handleHalStatus(retval, "getModelState");
@@ -276,8 +389,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public android.hardware.soundtrigger.V2_3.ModelParameterRange queryParameter(int modelHandle,
- int param) {
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
AtomicInteger status = new AtomicInteger(-1);
AtomicReference<android.hardware.soundtrigger.V2_3.OptionalModelParameterRange>
optionalRange =
@@ -298,25 +410,36 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return (optionalRange.get().getDiscriminator()
== android.hardware.soundtrigger.V2_3.OptionalModelParameterRange.hidl_discriminator.range)
?
- optionalRange.get().range() : null;
+ ConversionUtil.hidl2aidlModelParameterRange(optionalRange.get().range()) : null;
}
@Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mBinder.linkToDeath(recipient, cookie);
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ IHwBinder.DeathRecipient wrapper = cookie -> recipient.binderDied();
+ mDeathRecipientMap.put(recipient, wrapper);
+ mBinder.linkToDeath(wrapper, 0);
}
@Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mBinder.unlinkToDeath(recipient);
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mBinder.unlinkToDeath(mDeathRecipientMap.remove(recipient));
}
@Override
- public String interfaceDescriptor() throws RemoteException {
- return as2_0().interfaceDescriptor();
+ public String interfaceDescriptor() {
+ try {
+ return as2_0().interfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
- private android.hardware.soundtrigger.V2_3.Properties getProperties_2_0()
+ @Override
+ public void flushCallbacks() {
+ // This is a no-op. Only implemented for decorators.
+ }
+
+ private Properties getProperties_2_0()
throws RemoteException {
AtomicInteger retval = new AtomicInteger(-1);
AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
@@ -328,12 +451,13 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
properties.set(p);
});
handleHalStatus(retval.get(), "getProperties");
- return Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get());
+ return ConversionUtil.hidl2aidlProperties(
+ Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get()));
}
private int loadSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- Callback callback, int cookie)
+ ModelCallback callback)
throws RemoteException {
// Convert the soundModel to V2.0.
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 =
@@ -341,17 +465,18 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
- as2_0().loadSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie, (r, h) -> {
+ as2_0().loadSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0, (r, h) -> {
retval.set(r);
handle.set(h);
});
handleHalStatus(retval.get(), "loadSoundModel");
+ mModelCallbacks.put(handle.get(), callback);
return handle.get();
}
private int loadPhraseSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- Callback callback, int cookie)
+ ModelCallback callback)
throws RemoteException {
// Convert the soundModel to V2.0.
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
@@ -359,28 +484,28 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
- as2_0().loadPhraseSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie,
+ as2_0().loadPhraseSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0,
(r, h) -> {
retval.set(r);
handle.set(h);
});
handleHalStatus(retval.get(), "loadSoundModel");
+ mModelCallbacks.put(handle.get(), callback);
return handle.get();
}
private void startRecognition_2_1(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie) {
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
try {
try {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 =
Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config);
int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1,
- new SoundTriggerCallback(callback), cookie);
+ new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
handleHalStatus(retval, "startRecognition_2_1");
} catch (NotSupported e) {
// Fall-back to the 2.0 version:
- startRecognition_2_0(modelHandle, config, callback, cookie);
+ startRecognition_2_0(modelHandle, config);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -388,13 +513,12 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
private void startRecognition_2_0(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie)
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config)
throws RemoteException {
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config);
int retval = as2_0().startRecognition(modelHandle, config_2_0,
- new SoundTriggerCallback(callback), cookie);
+ new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
handleHalStatus(retval, "startRecognition");
}
@@ -427,6 +551,14 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return mUnderlying_2_3;
}
+ private @NonNull
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4() throws NotSupported {
+ if (mUnderlying_2_4 == null) {
+ throw new NotSupported("Underlying driver version < 2.4");
+ }
+ return mUnderlying_2_4;
+ }
+
/**
* A checked exception representing the requested interface version not being supported.
* At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
@@ -448,28 +580,48 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
}
- private static class SoundTriggerCallback extends
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
- private final @NonNull
- Callback mDelegate;
+ private static class GlobalCallbackWrapper extends
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.Stub {
+ private final @NonNull GlobalCallback mDelegate;
+
+ private GlobalCallbackWrapper(@NonNull GlobalCallback delegate) {
+ mDelegate = delegate;
+ }
- private SoundTriggerCallback(
- @NonNull Callback delegate) {
+ @Override
+ public void onResourcesAvailable() {
+ mDelegate.onResourcesAvailable();
+ }
+ }
+
+ private static class ModelCallbackWrapper extends
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.Stub {
+ private final @NonNull ModelCallback mDelegate;
+
+ private ModelCallbackWrapper(
+ @NonNull ModelCallback delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
@Override
+ public void modelUnloaded(int modelHandle) {
+ mDelegate.modelUnloaded(modelHandle);
+ }
+
+ @Override
public void recognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
int cookie) {
- mDelegate.recognitionCallback(event, cookie);
+ mDelegate.recognitionCallback(event.header.model,
+ ConversionUtil.hidl2aidlRecognitionEvent(event));
}
@Override
public void phraseRecognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
int cookie) {
- mDelegate.phraseRecognitionCallback(event, cookie);
+ mDelegate.phraseRecognitionCallback(event.common.header.model,
+ ConversionUtil.hidl2aidlPhraseRecognitionEvent(event));
}
@Override
@@ -485,7 +637,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
int cookie) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
Hw2CompatUtil.convertRecognitionEvent_2_0_to_2_1(event);
- mDelegate.recognitionCallback(event_2_1, cookie);
+ recognitionCallback_2_1(event_2_1, cookie);
}
@Override
@@ -494,7 +646,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
int cookie) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
event_2_1 = Hw2CompatUtil.convertPhraseRecognitionEvent_2_0_to_2_1(event);
- mDelegate.phraseRecognitionCallback(event_2_1, cookie);
+ phraseRecognitionCallback_2_1(event_2_1, cookie);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
deleted file mode 100644
index cf7460b306cd..000000000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.media.soundtrigger_middleware.Status;
-import android.os.DeadObjectException;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
- * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
- * common HAL malfunctions, to help track them and assist in debugging.
- *
- * The class is thread-safe.
- */
-public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
- static final String TAG = "SoundTriggerHw2Enforcer";
-
- final ISoundTriggerHw2 mUnderlying;
- Map<Integer, Boolean> mModelStates = new HashMap<>();
-
- public SoundTriggerHw2Enforcer(
- ISoundTriggerHw2 underlying) {
- mUnderlying = underlying;
- }
-
- @Override
- public Properties getProperties() {
- try {
- return mUnderlying.getProperties();
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
- int cookie) {
- try {
- int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
- }
- return handle;
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
- int cookie) {
- try {
- int handle = mUnderlying.loadPhraseSoundModel(soundModel,
- new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
- }
- return handle;
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void unloadSoundModel(int modelHandle) {
- try {
- mUnderlying.unloadSoundModel(modelHandle);
- synchronized (mModelStates) {
- mModelStates.remove(modelHandle);
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void stopRecognition(int modelHandle) {
- try {
- mUnderlying.stopRecognition(modelHandle);
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, false);
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void stopAllRecognitions() {
- try {
- mUnderlying.stopAllRecognitions();
- synchronized (mModelStates) {
- for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
- entry.setValue(false);
- }
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
- int cookie) {
- // It is possible that an event will be sent before the HAL returns from the
- // startRecognition call, thus it is important to set the state to active before the call.
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, true);
- }
- try {
- mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
- cookie);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void getModelState(int modelHandle) {
- try {
- mUnderlying.getModelState(modelHandle);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int getModelParameter(int modelHandle, int param) {
- try {
- return mUnderlying.getModelParameter(modelHandle, param);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void setModelParameter(int modelHandle, int param, int value) {
- try {
- mUnderlying.setModelParameter(modelHandle, param, value);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public ModelParameterRange queryParameter(int modelHandle, int param) {
- try {
- return mUnderlying.queryParameter(modelHandle, param);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mUnderlying.linkToDeath(recipient, cookie);
- }
-
- @Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mUnderlying.unlinkToDeath(recipient);
- }
-
- @Override
- public String interfaceDescriptor() throws RemoteException {
- return mUnderlying.interfaceDescriptor();
- }
-
- private static RuntimeException handleException(RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- // Server is dead, no need to reboot.
- Log.e(TAG, "HAL died");
- throw new RecoverableException(Status.DEAD_OBJECT);
- }
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
- rebootHal();
- throw e;
- }
-
- private static void rebootHal() {
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
- }
-
- private class CallbackEnforcer implements Callback {
- private final Callback mUnderlying;
-
- private CallbackEnforcer(
- Callback underlying) {
- mUnderlying = underlying;
- }
-
- @Override
- public void recognitionCallback(ISoundTriggerHwCallback.RecognitionEvent event,
- int cookie) {
- int model = event.header.model;
- synchronized (mModelStates) {
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- rebootHal();
- return;
- }
- if (event.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
- }
- }
- mUnderlying.recognitionCallback(event, cookie);
- }
-
- @Override
- public void phraseRecognitionCallback(ISoundTriggerHwCallback.PhraseRecognitionEvent event,
- int cookie) {
- int model = event.common.header.model;
- synchronized (mModelStates) {
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- rebootHal();
- return;
- }
- if (event.common.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
- }
- }
- mUnderlying.phraseRecognitionCallback(event, cookie);
- }
- }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
new file mode 100644
index 000000000000..f56475682be0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -0,0 +1,232 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+public class SoundTriggerHw3Compat implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHw mDriver;
+ private final @NonNull Runnable mRebootRunnable;
+
+ public SoundTriggerHw3Compat(@NonNull IBinder binder, @NonNull Runnable rebootRunnable) {
+ mDriver = android.hardware.soundtrigger3.ISoundTriggerHw.Stub.asInterface(binder);
+ mRebootRunnable = rebootRunnable;
+ }
+
+ @Override
+ public Properties getProperties() {
+ try {
+ return mDriver.getProperties();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ mDriver.registerGlobalCallback(new GlobalCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ try {
+ return mDriver.loadSoundModel(soundModel, new ModelCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ try {
+ return mDriver.loadPhraseSoundModel(soundModel, new ModelCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try {
+ mDriver.unloadSoundModel(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ try {
+ mDriver.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try {
+ mDriver.stopRecognition(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ try {
+ mDriver.forceRecognitionEvent(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try {
+ return mDriver.queryParameter(modelHandle, param);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try {
+ return mDriver.getParameter(modelHandle, param);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try {
+ mDriver.setParameter(modelHandle, param, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ try {
+ return mDriver.asBinder().getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ try {
+ mDriver.asBinder().linkToDeath(recipient, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDriver.asBinder().unlinkToDeath(recipient, 0);
+ }
+
+ @Override
+ public void flushCallbacks() {
+ // No-op.
+ }
+
+ @Override
+ public void reboot() {
+ mRebootRunnable.run();
+ }
+
+ @Override
+ public void detach() {
+ // No-op.
+ }
+
+ private static class GlobalCallbackAdaper extends ISoundTriggerHwGlobalCallback.Stub {
+ private final @NonNull GlobalCallback mDelegate;
+
+ public GlobalCallbackAdaper(@NonNull GlobalCallback callback) {
+ mDelegate = callback;
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ mDelegate.onResourcesAvailable();
+ }
+ }
+
+ private static class ModelCallbackAdaper extends ISoundTriggerHwCallback.Stub {
+ private final @NonNull ModelCallback mDelegate;
+
+ public ModelCallbackAdaper(ModelCallback callback) {
+ mDelegate = callback;
+ }
+
+ @Override
+ public void modelUnloaded(int model) {
+ mDelegate.modelUnloaded(model);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+ mDelegate.phraseRecognitionCallback(model, event);
+ }
+
+ @Override
+ public void recognitionCallback(int model, RecognitionEvent event) {
+ mDelegate.recognitionCallback(model, event);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index d76b1bf71a1c..c8c0f3d00cbf 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -17,12 +17,9 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
-import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
@@ -39,7 +36,7 @@ import java.util.List;
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
* <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
* recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status}
+ * {@link android.media.soundtrigger.Status}
* constants. Any other exception thrown should be regarded as a bug in the implementation or one
* of its dependencies (assuming correct usage).
* <li>The implementation is designed for testibility by featuring dependency injection (the
@@ -84,15 +81,15 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareIntern
@NonNull AudioSessionProvider audioSessionProvider) {
List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
- for (int i = 0; i < halFactories.length; ++i) {
+ for (HalFactory halFactory : halFactories) {
try {
- modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider));
+ modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
} catch (Exception e) {
Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
}
}
- mModules = modules.toArray(new SoundTriggerModule[modules.size()]);
+ mModules = modules.toArray(new SoundTriggerModule[0]);
}
/**
@@ -122,18 +119,4 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareIntern
ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
return mModules[handle].attach(callback);
}
-
- @Override
- public void setCaptureState(boolean active) {
- for (SoundTriggerModule module : mModules) {
- module.setExternalCaptureState(active);
- }
- }
-
- @Override
- public @NonNull
- IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 2ef0759719fc..559e777a8c0e 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,16 +18,16 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.IBinder;
import android.os.RemoteException;
@@ -57,9 +57,10 @@ import java.util.Objects;
* }
* }
* </code></pre>
- * The actual handling of these events is then done inside of {@link #logReturnWithObject(Object,
- * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
- * #logExceptionWithObject(Object, String, Exception, Object[])}.
+ * The actual handling of these events is then done inside of
+ * {@link #logReturnWithObject(Object, Identity, String, Object, Object[])},
+ * {@link #logVoidReturnWithObject(Object, Identity, String, Object[])} and {@link
+ * #logExceptionWithObject(Object, Identity, String, Exception, Object[])}.
*/
public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareLogging";
@@ -96,23 +97,6 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
}
- @Override
- public void setCaptureState(boolean active) throws RemoteException {
- try {
- mDelegate.setCaptureState(active);
- logVoidReturn("setCaptureState", active);
- } catch (Exception e) {
- logException("setCaptureState", e, active);
- throw e;
- }
- }
-
- @Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
@@ -298,10 +282,10 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public void onRecognition(int modelHandle, RecognitionEvent event)
+ public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
try {
- mCallbackDelegate.onRecognition(modelHandle, event);
+ mCallbackDelegate.onRecognition(modelHandle, event, captureSession);
logVoidReturn("onRecognition", modelHandle, event);
} catch (Exception e) {
logException("onRecognition", e, modelHandle, event);
@@ -310,10 +294,11 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
try {
- mCallbackDelegate.onPhraseRecognition(modelHandle, event);
+ mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession);
logVoidReturn("onPhraseRecognition", modelHandle, event);
} catch (Exception e) {
logException("onPhraseRecognition", e, modelHandle, event);
@@ -322,12 +307,23 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ try {
+ mCallbackDelegate.onModelUnloaded(modelHandle);
+ logVoidReturn("onModelUnloaded", modelHandle);
+ } catch (Exception e) {
+ logException("onModelUnloaded", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onResourcesAvailable() throws RemoteException {
try {
- mCallbackDelegate.onRecognitionAvailabilityChange(available);
- logVoidReturn("onRecognitionAvailabilityChange", available);
+ mCallbackDelegate.onResourcesAvailable();
+ logVoidReturn("onResourcesAvailable");
} catch (Exception e) {
- logException("onRecognitionAvailabilityChange", e, available);
+ logException("onResourcesAvailable", e);
throw e;
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index c1f8240f4018..32ba852634be 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -24,20 +24,20 @@ import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.PermissionChecker;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.permission.PermissionUtil;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -90,24 +90,12 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
return wrapper.attach(mDelegate.attach(handle, wrapper.getCallbackWrapper()));
}
- @Override
- public void setCaptureState(boolean active) throws RemoteException {
- // This is an internal call. No permissions needed.
- mDelegate.setCaptureState(active);
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
return Objects.toString(mDelegate);
}
- @Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
/**
* Get the identity context, or throws an InternalServerError if it has not been established.
*
@@ -313,22 +301,28 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
}
@Override
- public void onRecognition(int modelHandle, RecognitionEvent event)
+ public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
enforcePermissions("Sound trigger recognition.");
- mDelegate.onRecognition(modelHandle, event);
+ mDelegate.onRecognition(modelHandle, event, captureSession);
}
@Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
enforcePermissions("Sound trigger phrase recognition.");
- mDelegate.onPhraseRecognition(modelHandle, event);
+ mDelegate.onPhraseRecognition(modelHandle, event, captureSession);
+ }
+
+ @Override
+ public void onResourcesAvailable() throws RemoteException {
+ mDelegate.onResourcesAvailable();
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
- mDelegate.onRecognitionAvailabilityChange(available);
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ mDelegate.onModelUnloaded(modelHandle);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index db7a575b08e2..1995e5497e55 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,7 +20,10 @@ import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.PermissionUtil;
@@ -28,13 +31,8 @@ import android.media.permission.SafeCloseable;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.RemoteException;
-import android.util.Log;
import com.android.server.SystemService;
@@ -79,13 +77,6 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
@NonNull Context context) {
mDelegate = Objects.requireNonNull(delegate);
mContext = context;
- new ExternalCaptureStateTracker(active -> {
- try {
- mDelegate.setCaptureState(active);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- });
}
@Override
@@ -232,23 +223,15 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
@Override
public void onStart() {
- HalFactory[] factories = new HalFactory[]{() -> {
- try {
- Log.d(TAG, "Connecting to default ISoundTriggerHw");
- return ISoundTriggerHw.getService(true);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }};
+ HalFactory[] factories = new HalFactory[]{new DefaultHalFactory()};
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
- new SoundTriggerMiddlewareService(
- new SoundTriggerMiddlewareLogging(
- new SoundTriggerMiddlewarePermission(
- new SoundTriggerMiddlewareValidation(
- new SoundTriggerMiddlewareImpl(factories,
- new AudioSessionProviderImpl())),
- getContext())), getContext()));
+ new SoundTriggerMiddlewareService(new SoundTriggerMiddlewareLogging(
+ new SoundTriggerMiddlewarePermission(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl())),
+ getContext())), getContext()));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 95a30c7f0278..d9fa79211a89 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -18,21 +18,21 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -46,10 +46,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
/**
* This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces correct usage by
@@ -89,7 +85,8 @@ import java.util.concurrent.atomic.AtomicReference;
* }
* </pre></code>
* Following this patterns ensures a consistent and rigorous handling of all aspects associated
- * with client-server separation.
+ * with client-server separation. Notable exceptions are stopRecognition() and unloadModel(), which
+ * follow slightly more complicated rules for synchronization (see README.md for details).
* <p>
* <b>Exception handling approach:</b><br>
* We make sure all client faults (argument and state validation) happen first, and
@@ -115,21 +112,18 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
private class ModuleState {
- final @NonNull SoundTriggerModuleProperties properties;
- Set<Session> sessions = new HashSet<>();
+ public @NonNull Properties properties;
+ public Set<Session> sessions = new HashSet<>();
- private ModuleState(@NonNull SoundTriggerModuleProperties properties) {
+ private ModuleState(@NonNull Properties properties) {
this.properties = properties;
}
}
- private AtomicReference<Boolean> mCaptureState = new AtomicReference<>();
-
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
private Map<Integer, ModuleState> mModules;
- public SoundTriggerMiddlewareValidation(
- @NonNull ISoundTriggerMiddlewareInternal delegate) {
+ public SoundTriggerMiddlewareValidation(@NonNull ISoundTriggerMiddlewareInternal delegate) {
mDelegate = delegate;
}
@@ -137,8 +131,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* Generic exception handling for exceptions thrown by the underlying implementation.
*
* Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
- * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
- * (<b>not</b> passed by Binder to the caller).
+ * by Binder to the caller) and <i>any other</i> exception as a {@link ServiceSpecificException}
+ * with a {@link Status#INTERNAL_ERROR} code.
* <p>
* Typical usage:
* <code><pre>
@@ -149,8 +143,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* }
* </pre></code>
*/
- static @NonNull
- RuntimeException handleException(@NonNull Exception e) {
+ static @NonNull RuntimeException handleException(@NonNull Exception e) {
if (e instanceof RecoverableException) {
throw new ServiceSpecificException(((RecoverableException) e).errorCode,
e.getMessage());
@@ -161,8 +154,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
@Override
- public @NonNull
- SoundTriggerModuleDescriptor[] listModules() {
+ public @NonNull SoundTriggerModuleDescriptor[] listModules() {
// Input validation (always valid).
synchronized (this) {
@@ -186,6 +178,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
throw new RuntimeException(
"listModules must always return the same result.");
}
+ mModules.get(desc.handle).properties = desc.properties;
}
}
return result;
@@ -223,23 +216,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
}
- @Override
- public void setCaptureState(boolean active) {
- // This is an internal call. No permissions needed.
- //
- // Normally, we would acquire a lock here. However, we do not access any state here so it
- // is safe to not lock. This call is typically done from a different context than all the
- // other calls and may result in a deadlock if we lock here (between the audio server and
- // the system server).
- try {
- mDelegate.setCaptureState(active);
- } catch (Exception e) {
- throw handleException(e);
- } finally {
- mCaptureState.set(active);
- }
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
@@ -247,17 +223,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
@Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
- @Override
public void dump(PrintWriter pw) {
synchronized (this) {
- Boolean captureState = mCaptureState.get();
- pw.printf("Capture state is %s\n\n", captureState == null ? "uninitialized"
- : (captureState ? "active" : "inactive"));
if (mModules != null) {
for (int handle : mModules.keySet()) {
final ModuleState module = mModules.get(handle);
@@ -303,10 +270,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* delivered to the caller (most commonly, for permission reasons).
*/
INTERCEPTED,
+ /**
+ * Model has been preemptively unloaded by the HAL.
+ */
+ PREEMPTED,
}
/** Activity state. */
- private AtomicInteger mActivityState = new AtomicInteger(Activity.LOADED.ordinal());
+ Activity activityState = Activity.LOADED;
/** Human-readable description of the model. */
final String description;
@@ -316,7 +287,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* parameter is supported. A null value means it is known to not be supported. A non-null
* value indicates the valid value range.
*/
- private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
+ private final Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
/**
* Check that the given parameter is known to be supported for this model.
@@ -351,24 +322,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
"value");
}
-
- /**
- * Update support state for the given parameter for this model.
- *
- * @param modelParam The parameter key.
- * @param range The parameter value range, or null if not supported.
- */
- void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
- parameterSupport.put(modelParam, range);
- }
-
- Activity getActivityState() {
- return Activity.values()[mActivityState.get()];
- }
-
- void setActivityState(Activity activity) {
- mActivityState.set(activity.ordinal());
- }
}
/**
@@ -377,13 +330,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
*/
private class Session extends ISoundTriggerModule.Stub {
private ISoundTriggerModule mDelegate;
- // While generally all the fields of this class must be changed under a lock, an exception
- // is made for the specific case of changing a model state from ACTIVE to LOADED, which
- // may happen as result of a recognition callback. This would happen atomically and is
- // necessary in order to avoid deadlocks associated with locking from within callbacks
- // possibly originating from the audio server.
- private @NonNull
- ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
+ private final @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
private final int mHandle;
private ModuleStatus mState = ModuleStatus.ALIVE;
private final CallbackWrapper mCallbackWrapper;
@@ -451,7 +398,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void unloadModel(int modelHandle) {
// Input validation (always valid).
-
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
if (mState == ModuleStatus.DETACHED) {
@@ -462,18 +408,24 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+ // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+ if (modelState.activityState != ModelState.Activity.LOADED
+ && modelState.activityState != ModelState.Activity.PREEMPTED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for unloading");
}
+ }
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.unloadModel(modelHandle);
- mLoadedModels.remove(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
+ // From here on, every exception isn't client's fault.
+ try {
+ // Calling the delegate must be done outside the lock.
+ mDelegate.unloadModel(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ mLoadedModels.remove(modelHandle);
}
}
@@ -492,20 +444,19 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+ ModelState.Activity activityState = modelState.activityState;
+ // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+ if (activityState != ModelState.Activity.LOADED
+ && activityState != ModelState.Activity.PREEMPTED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for starting recognition");
}
// From here on, every exception isn't client's fault.
try {
- // Normally, we would set the state after the operation succeeds. However, since
- // the activity state may be reset outside of the lock, we set it here first,
- // and reset it in case of exception.
- modelState.setActivityState(ModelState.Activity.ACTIVE);
mDelegate.startRecognition(modelHandle, config);
+ modelState.activityState = ModelState.Activity.ACTIVE;
} catch (Exception e) {
- modelState.setActivityState(ModelState.Activity.LOADED);
throw handleException(e);
}
}
@@ -529,17 +480,36 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
- // If the activity state is LOADED or INTERCEPTED, we skip delegating the
- // command, but still consider the call valid. In either case, the resulting
- // state is LOADED.
- if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(modelHandle);
+ // If the activity state is INTERCEPTED, we skip delegating the command, but
+ // still consider the call valid.
+ if (modelState.activityState == ModelState.Activity.INTERCEPTED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ return;
}
- modelState.setActivityState(ModelState.Activity.LOADED);
} catch (Exception e) {
throw handleException(e);
}
}
+
+ // Calling the delegate's stop must be done without the lock.
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState == null) {
+ // The model was unloaded while we let go of the lock.
+ return;
+ }
+
+ // After the call, the state is LOADED, unless it has been first preempted.
+ if (modelState.activityState != ModelState.Activity.PREEMPTED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
+ }
}
@Override
@@ -562,7 +532,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
// If the activity state is LOADED or INTERCEPTED, we skip delegating the
// command, but still consider the call valid.
- if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
+ if (modelState.activityState == ModelState.Activity.ACTIVE) {
mDelegate.forceRecognitionEvent(modelHandle);
}
} catch (Exception e) {
@@ -644,7 +614,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
modelParam);
- modelState.updateParameterSupport(modelParam, result);
+ modelState.parameterSupport.put(modelParam, result);
return result;
} catch (Exception e) {
throw handleException(e);
@@ -702,7 +672,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
pw.print(entry.getKey());
pw.print('\t');
- pw.print(entry.getValue().getActivityState().name());
+ pw.print(entry.getValue().activityState.name());
pw.print('\t');
pw.print(entry.getValue().description);
pw.println();
@@ -732,70 +702,79 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
@Override
- public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
- // might be coming from the audio server (via setCaptureState()) while it is holding
- // a lock that is also acquired while loading / unloading models. Thus, we require a
- // strict locking order here, where obtaining our lock must always come first.
- // To avoid this problem, we use an atomic model activity state. There is a risk of the
- // model not being in the mLoadedModels map here, since it might have been stopped /
- // unloaded while the event was in flight.
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- if (event.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.LOADED);
+ public void onRecognition(int modelHandle, @NonNull RecognitionEvent event,
+ int captureSession) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
}
+
+ // Calling the delegate callback must be done outside the lock.
try {
- mCallback.onRecognition(modelHandle, event);
+ mCallback.onRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- if (event.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ Log.w(TAG, "Client callback exception.", e);
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.INTERCEPTED;
+ }
}
}
}
- }
- @Override
- public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
- // might be coming from the audio server (via setCaptureState()) while it is holding
- // a lock that is also acquired while loading / unloading models. Thus, we require a
- // strict locking order here, where obtaining our lock must always come first.
- // To avoid this problem, we use an atomic model activity state. There is a risk of the
- // model not being in the mLoadedModels map here, since it might have been stopped /
- // unloaded while the event was in flight.
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- if (event.common.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.LOADED);
+ @Override
+ public void onPhraseRecognition(int modelHandle,
+ @NonNull PhraseRecognitionEvent event, int captureSession) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
}
+
+ // Calling the delegate callback must be done outside the lock.
try {
- mCallback.onPhraseRecognition(modelHandle, event);
+ mCallback.onPhraseRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- if (event.common.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ Log.w(TAG, "Client callback exception.", e);
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.INTERCEPTED;
+ }
}
}
}
- }
- @Override
- public void onRecognitionAvailabilityChange(boolean available) {
- // Not locking to avoid deadlocks (not affecting any state).
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ @Override
+ public void onModelUnloaded(int modelHandle) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ modelState.activityState = ModelState.Activity.PREEMPTED;
+ }
+
+ // Calling the delegate callback must be done outside the lock.
+ try {
+ mCallback.onModelUnloaded(modelHandle);
+ } catch (Exception e) {
+ Log.w(TAG, "Client callback exception.", e);
+ }
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ // Not locking to avoid deadlocks (not affecting any state).
+ try {
+ mCallback.onResourcesAvailable();
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
}
- }
@Override
public void onModuleDied() {
@@ -820,8 +799,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// Gracefully stop all active recognitions and unload the models.
for (Map.Entry<Integer, ModelState> entry :
mLoadedModels.entrySet()) {
- if (entry.getValue().getActivityState()
- == ModelState.Activity.ACTIVE) {
+ if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
mDelegate.stopRecognition(entry.getKey());
}
mDelegate.unloadModel(entry.getKey());
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 02d978dfdf99..f2111581d340 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -18,30 +18,24 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
-import android.os.IHwBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -71,9 +65,8 @@ import java.util.Set;
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
* <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
* recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status} constants. Any other exception
- * thrown should be regarded as a bug in the implementation or one of its dependencies
- * (assuming correct usage).
+ * {@link android.media.soundtrigger.Status} constants. Any other exception thrown should be
+ * regarded as a bug in the implementation or one of its dependencies (assuming correct usage).
* <li>The implementation is designed for testability by featuring dependency injection (the
* underlying HAL driver instances are passed to the ctor) and by minimizing dependencies
* on Android runtime.
@@ -88,15 +81,13 @@ import java.util.Set;
*
* @hide
*/
-class SoundTriggerModule implements IHwBinder.DeathRecipient {
+class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.GlobalCallback {
static private final String TAG = "SoundTriggerModule";
- @NonNull private HalFactory mHalFactory;
- @NonNull private ISoundTriggerHw2 mHalService;
+ @NonNull private final HalFactory mHalFactory;
+ @NonNull private ISoundTriggerHal mHalService;
@NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
private final Set<Session> mActiveSessions = new HashSet<>();
- private int mNumLoadedModels = 0;
- private SoundTriggerModuleProperties mProperties;
- private boolean mRecognitionAvailable;
+ private Properties mProperties;
/**
* Ctor.
@@ -110,9 +101,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
mAudioSessionProvider = audioSessionProvider;
attachToHal();
- mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
- // We conservatively assume that external capture is active until explicitly told otherwise.
- mRecognitionAvailable = mProperties.concurrentCapture;
}
/**
@@ -140,50 +128,13 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
* @return The properties structure.
*/
synchronized @NonNull
- SoundTriggerModuleProperties getProperties() {
+ Properties getProperties() {
return mProperties;
}
- /**
- * Notify the module that external capture has started / finished, using the same input device
- * used for recognition.
- * If the underlying driver does not support recognition while capturing, capture will be
- * aborted, and the recognition callback will receive and abort event. In addition, all active
- * clients will be notified of the change in state.
- *
- * @param active true iff external capture is active.
- */
- void setExternalCaptureState(boolean active) {
- // We should never invoke callbacks while holding the lock, since this may deadlock with
- // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
- // the lock, but invoke them after releasing it.
- List<Runnable> callbacks = new LinkedList<>();
-
- synchronized (this) {
- if (mProperties.concurrentCapture) {
- // If we support concurrent capture, we don't care about any of this.
- return;
- }
- mRecognitionAvailable = !active;
- if (!mRecognitionAvailable) {
- // Our module does not support recognition while a capture is active -
- // need to abort all active recognitions.
- for (Session session : mActiveSessions) {
- session.abortActiveRecognitions(callbacks);
- }
- }
- }
- for (Runnable callback : callbacks) {
- callback.run();
- }
- for (Session session : mActiveSessions) {
- session.notifyRecognitionAvailability();
- }
- }
-
@Override
- public void serviceDied(long cookie) {
- Log.w(TAG, String.format("Underlying HAL driver died."));
+ public void binderDied() {
+ Log.w(TAG, "Underlying HAL driver died.");
List<ISoundTriggerCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<>(mActiveSessions.size());
@@ -207,20 +158,19 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
* Resets the transient state of this object.
*/
private void reset() {
+ mHalService.detach();
attachToHal();
- // We conservatively assume that external capture is active until explicitly told otherwise.
- mRecognitionAvailable = mProperties.concurrentCapture;
- mNumLoadedModels = 0;
}
/**
* Attached to the HAL service via factory.
*/
private void attachToHal() {
- mHalService = new SoundTriggerHw2Enforcer(
- new SoundTriggerHw2Watchdog(
- new SoundTriggerHw2Compat(mHalFactory.create())));
- mHalService.linkToDeath(this, 0);
+ mHalService = new SoundTriggerHalEnforcer(
+ new SoundTriggerHalWatchdog(mHalFactory.create()));
+ mHalService.linkToDeath(this);
+ mHalService.registerCallback(this);
+ mProperties = mHalService.getProperties();
}
/**
@@ -232,6 +182,25 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
mActiveSessions.remove(session);
}
+ @Override
+ public void onResourcesAvailable() {
+ List<ISoundTriggerCallback> callbacks;
+ synchronized (this) {
+ callbacks = new ArrayList<>(mActiveSessions.size());
+ for (Session session : mActiveSessions) {
+ callbacks.add(session.mCallback);
+ }
+ }
+ // Trigger the callbacks outside of the lock to avoid deadlocks.
+ for (ISoundTriggerCallback callback : callbacks) {
+ try {
+ callback.onResourcesAvailable();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
/** State of a single sound model. */
private enum ModelState {
/** Initial state, until load() is called. */
@@ -249,7 +218,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*/
private class Session implements ISoundTriggerModule {
private ISoundTriggerCallback mCallback;
- private Map<Integer, Model> mLoadedModels = new HashMap<>();
+ private final Map<Integer, Model> mLoadedModels = new HashMap<>();
/**
* Ctor.
@@ -258,7 +227,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*/
private Session(@NonNull ISoundTriggerCallback callback) {
mCallback = callback;
- notifyRecognitionAvailability();
}
@Override
@@ -274,95 +242,65 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@Override
public int loadModel(@NonNull SoundModel model) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
- mAudioSessionProvider.acquireSession();
-
- try {
- synchronized (SoundTriggerModule.this) {
+ synchronized (SoundTriggerModule.this) {
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+ try {
checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
- }
Model loadedModel = new Model();
- int result = loadedModel.load(model, audioSession);
- ++mNumLoadedModels;
- return result;
- }
- } catch (Exception e) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote
- // process that provides the audio sessions, which may also be calling into us.
- try {
- mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
- } catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ return loadedModel.load(model, audioSession);
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ try {
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ } catch (Exception ee) {
+ Log.e(TAG, "Failed to release session.", ee);
+ }
+ throw e;
}
- throw e;
}
}
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
- mAudioSessionProvider.acquireSession();
-
- try {
- synchronized (SoundTriggerModule.this) {
+ synchronized (SoundTriggerModule.this) {
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+ try {
checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
- }
Model loadedModel = new Model();
int result = loadedModel.load(model, audioSession);
- ++mNumLoadedModels;
Log.d(TAG, String.format("loadPhraseModel()->%d", result));
return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ try {
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ } catch (Exception ee) {
+ Log.e(TAG, "Failed to release session.", ee);
+ }
+ throw e;
}
- } catch (Exception e) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote
- // process that provides the audio sessions, which may also be calling into us.
- try {
- mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
- } catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
- }
- throw e;
}
}
@Override
public void unloadModel(int modelHandle) {
- int sessionId;
synchronized (SoundTriggerModule.this) {
+ int sessionId;
checkValid();
sessionId = mLoadedModels.get(modelHandle).unload();
- --mNumLoadedModels;
+ mAudioSessionProvider.releaseSession(sessionId);
}
-
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- mAudioSessionProvider.releaseSession(sessionId);
}
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- // We should never invoke callbacks while holding the lock, since this may deadlock with
- // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
- // the lock, but invoke them after releasing it.
- List<Runnable> callbacks = new LinkedList<>();
-
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).startRecognition(config, callbacks);
- }
-
- for (Runnable callback : callbacks) {
- callback.run();
+ mLoadedModels.get(modelHandle).startRecognition(config);
}
}
@@ -407,27 +345,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
/**
- * Abort all currently active recognitions.
- * @param callbacks Will be appended with a list of callbacks that need to be invoked
- * after this method returns, without holding the module lock.
- */
- private void abortActiveRecognitions(@NonNull List<Runnable> callbacks) {
- for (Model model : mLoadedModels.values()) {
- model.abortActiveRecognition(callbacks);
- }
- }
-
- private void notifyRecognitionAvailability() {
- try {
- mCallback.onRecognitionAvailabilityChange(mRecognitionAvailable);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
- }
-
- /**
* The underlying module HAL is dead.
* @return The client callback that needs to be invoked to notify the client.
*/
@@ -455,10 +372,9 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*
* All model-based operations are delegated to this class and implemented here.
*/
- private class Model implements ISoundTriggerHw2.Callback {
+ private class Model implements ISoundTriggerHal.ModelCallback {
public int mHandle;
private ModelState mState = ModelState.INIT;
- private int mModelType = SoundModelType.UNKNOWN;
private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession;
private @NonNull
@@ -473,11 +389,8 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
private int load(@NonNull SoundModel model,
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
- mModelType = model.type;
mSession = audioSession;
- ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
-
- mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
+ mHandle = mHalService.loadSoundModel(model, this);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
@@ -485,12 +398,8 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
private int load(@NonNull PhraseSoundModel model,
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
- mModelType = model.common.type;
mSession = audioSession;
- ISoundTriggerHw.PhraseSoundModel hidlModel =
- ConversionUtil.aidl2hidlPhraseSoundModel(model);
-
- mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
+ mHandle = mHalService.loadPhraseSoundModel(model, this);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
@@ -507,18 +416,9 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
return mSession.mSessionHandle;
}
- private void startRecognition(@NonNull RecognitionConfig config,
- @NonNull List<Runnable> callbacks) {
- if (!mRecognitionAvailable) {
- // Recognition is unavailable - send an abort event immediately.
- callbacks.add(this::notifyAbort);
- return;
- }
- android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
- ConversionUtil.aidl2hidlRecognitionConfig(config);
- hidlConfig.base.header.captureDevice = mSession.mDeviceHandle;
- hidlConfig.base.header.captureHandle = mSession.mIoHandle;
- mHalService.startRecognition(mHandle, hidlConfig, this, 0);
+ private void startRecognition(@NonNull RecognitionConfig config) {
+ mHalService.startRecognition(mHandle, mSession.mDeviceHandle,
+ mSession.mIoHandle, config);
setState(ModelState.ACTIVE);
}
@@ -537,7 +437,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
// This call is idempotent in order to avoid races.
return;
}
- mHalService.getModelState(mHandle);
+ mHalService.forceRecognitionEvent(mHandle);
}
@@ -553,78 +453,47 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@Nullable
private ModelParameterRange queryModelParameterSupport(int modelParam) {
- return ConversionUtil.hidl2aidlModelParameterRange(
- mHalService.queryParameter(mHandle,
- ConversionUtil.aidl2hidlModelParameter(modelParam)));
+ return mHalService.queryParameter(mHandle, modelParam);
}
- /**
- * Abort the recognition, if active.
- * @param callbacks Will be appended with a list of callbacks that need to be invoked
- * after this method returns, without holding the module lock.
- */
- private void abortActiveRecognition(List<Runnable> callbacks) {
- // If we're inactive, do nothing.
- if (getState() != ModelState.ACTIVE) {
- return;
+ @Override
+ public void recognitionCallback(int modelHandle,
+ @NonNull RecognitionEvent recognitionEvent) {
+ ISoundTriggerCallback callback;
+ synchronized (SoundTriggerModule.this) {
+ if (recognitionEvent.status != RecognitionStatus.FORCED) {
+ setState(ModelState.LOADED);
+ }
+ callback = mCallback;
}
- // Stop recognition.
- stopRecognition();
-
- // Notify the client that recognition has been aborted.
- callbacks.add(this::notifyAbort);
- }
-
- /** Notify the client that recognition has been aborted. */
- private void notifyAbort() {
+ // The callback must be invoked outside of the lock.
try {
- switch (mModelType) {
- case SoundModelType.GENERIC: {
- android.media.soundtrigger_middleware.RecognitionEvent event =
- newEmptyRecognitionEvent();
- event.status =
- android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
- event.type = SoundModelType.GENERIC;
- mCallback.onRecognition(mHandle, event);
- }
- break;
-
- case SoundModelType.KEYPHRASE: {
- android.media.soundtrigger_middleware.PhraseRecognitionEvent event =
- newEmptyPhraseRecognitionEvent();
- event.common.status =
- android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
- event.common.type = SoundModelType.KEYPHRASE;
- mCallback.onPhraseRecognition(mHandle, event);
- }
- break;
-
- default:
- Log.e(TAG, "Unknown model type: " + mModelType);
-
+ if (callback != null) {
+ callback.onRecognition(mHandle, recognitionEvent, mSession.mSessionHandle);
}
} catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
}
}
@Override
- public void recognitionCallback(
- @NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
- int cookie) {
- RecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
- aidlEvent.captureSession = mSession.mSessionHandle;
+ public void phraseRecognitionCallback(int modelHandle,
+ @NonNull PhraseRecognitionEvent phraseRecognitionEvent) {
+ ISoundTriggerCallback callback;
synchronized (SoundTriggerModule.this) {
- if (aidlEvent.status != RecognitionStatus.FORCED) {
+ if (phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
+ callback = mCallback;
}
+
// The callback must be invoked outside of the lock.
try {
- mCallback.onRecognition(mHandle, aidlEvent);
+ if (callback != null) {
+ mCallback.onPhraseRecognition(mHandle, phraseRecognitionEvent,
+ mSession.mSessionHandle);
+ }
} catch (RemoteException e) {
// We're not expecting any exceptions here.
throw e.rethrowAsRuntimeException();
@@ -632,22 +501,17 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
@Override
- public void phraseRecognitionCallback(
- @NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
- int cookie) {
- PhraseRecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
- aidlEvent.common.captureSession = mSession.mSessionHandle;
-
+ public void modelUnloaded(int modelHandle) {
+ ISoundTriggerCallback callback;
synchronized (SoundTriggerModule.this) {
- if (aidlEvent.common.status != RecognitionStatus.FORCED) {
- setState(ModelState.LOADED);
- }
+ callback = mCallback;
}
// The callback must be invoked outside of the lock.
try {
- mCallback.onPhraseRecognition(mHandle, aidlEvent);
+ if (callback != null) {
+ callback.onModelUnloaded(modelHandle);
+ }
} catch (RemoteException e) {
// We're not expecting any exceptions here.
throw e.rethrowAsRuntimeException();
@@ -655,33 +519,4 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
}
}
-
- /**
- * Creates a default-initialized recognition event.
- *
- * Non-nullable object fields are default constructed.
- * Non-nullable array fields are initialized to 0 length.
- *
- * @return The event.
- */
- private static RecognitionEvent newEmptyRecognitionEvent() {
- RecognitionEvent result = new RecognitionEvent();
- result.data = new byte[0];
- return result;
- }
-
- /**
- * Creates a default-initialized phrase recognition event.
- *
- * Non-nullable object fields are default constructed.
- * Non-nullable array fields are initialized to 0 length.
- *
- * @return The event.
- */
- private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() {
- PhraseRecognitionEvent result = new PhraseRecognitionEvent();
- result.common = newEmptyRecognitionEvent();
- result.phraseExtras = new PhraseRecognitionExtra[0];
- return result;
- }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
index e05c468186ed..4d5212114907 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -17,21 +17,18 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-
-import com.android.internal.util.Preconditions;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
import java.util.Objects;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Utilities for asserting the validity of various data types used by this module.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a436e6b3787b..d95e826339a1 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -22,6 +22,7 @@ import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -132,7 +133,7 @@ public interface StatusBarManagerInternal {
/** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */
void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen);
+ @Behavior int behavior, InsetsState requestedState, String packageName);
/** @see com.android.internal.statusbar.IStatusBar#showTransient */
void showTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3a7e13b8d872..47fdc4e5179a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -59,6 +59,7 @@ import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -526,13 +527,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
getUiState(displayId).setBarAttributes(appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedState, packageName);
if (mBar != null) {
try {
mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedState, packageName);
} catch (RemoteException ex) { }
}
}
@@ -1103,13 +1104,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
return state;
}
- private class UiState {
+ private static class UiState {
private @Appearance int mAppearance = 0;
private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
- private ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
+ private final ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
private boolean mNavbarColorManagedByIme = false;
private @Behavior int mBehavior;
- private boolean mFullscreen = false;
+ private InsetsState mRequestedState = new InsetsState();
+ private String mPackageName = "none";
private int mDisabled1 = 0;
private int mDisabled2 = 0;
private int mImeWindowVis = 0;
@@ -1119,12 +1121,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private void setBarAttributes(@Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
mAppearance = appearance;
mAppearanceRegions = appearanceRegions;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
- mFullscreen = isFullscreen;
+ mRequestedState = requestedState;
+ mPackageName = packageName;
}
private void showTransient(@InternalInsetsType int[] types) {
@@ -1244,8 +1247,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
- state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen,
- transientBarTypes);
+ state.mNavbarColorManagedByIme, state.mBehavior, state.mRequestedState,
+ state.mPackageName, transientBarTypes);
}
}
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 8d79a81fe11e..0b34eb8234f0 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -368,16 +368,12 @@ public final class StorageSessionController {
mExternalStorageServicePackageName = provider.applicationInfo.packageName;
mExternalStorageServiceAppId = UserHandle.getAppId(provider.applicationInfo.uid);
- Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE);
- intent.setPackage(mExternalStorageServicePackageName);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
- if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ ServiceInfo serviceInfo = resolveExternalStorageServiceAsUser(UserHandle.USER_SYSTEM);
+ if (serviceInfo == null) {
throw new ExternalStorageServiceException(
"No valid ExternalStorageService component found");
}
- ServiceInfo serviceInfo = resolveInfo.serviceInfo;
ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
if (!Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE
.equals(serviceInfo.permission)) {
@@ -490,4 +486,24 @@ public final class StorageSessionController {
private boolean shouldHandle(@Nullable VolumeInfo vol) {
return !mIsResetting && (vol == null || isSupportedVolume(vol));
}
+
+ /**
+ * Returns {@code true} if the given user supports external storage,
+ * {@code false} otherwise.
+ */
+ public boolean supportsExternalStorage(int userId) {
+ return resolveExternalStorageServiceAsUser(userId) != null;
+ }
+
+ private ServiceInfo resolveExternalStorageServiceAsUser(int userId) {
+ Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE);
+ intent.setPackage(mExternalStorageServicePackageName);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveServiceAsUser(intent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId);
+ if (resolveInfo == null) {
+ return null;
+ }
+
+ return resolveInfo.serviceInfo;
+ }
}
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
new file mode 100644
index 000000000000..f1bfea760792
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.timedetector."
+ }
+ ]
+ },
+ {
+ "name": "CtsTimeTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 357c23222658..e751a7bf2c26 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -42,6 +42,7 @@ import com.android.server.timezonedetector.ArrayMapWithHistory;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ReferenceWithHistory;
+import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Objects;
@@ -321,12 +322,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
+ mEnvironment.isAutoTimeDetectionEnabled());
- ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
- ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
+ ipw.printf("mEnvironment.elapsedRealtimeMillis()=%s (%s)\n",
+ Duration.ofMillis(elapsedRealtimeMillis), elapsedRealtimeMillis);
+ long systemClockMillis = mEnvironment.systemClockMillis();
+ ipw.printf("mEnvironment.systemClockMillis()=%s (%s)\n",
+ Instant.ofEpochMilli(systemClockMillis), systemClockMillis);
ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ mEnvironment.systemClockUpdateThresholdMillis());
Instant autoTimeLowerBound = mEnvironment.autoTimeLowerBound();
- ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+ ipw.printf("mEnvironment.autoTimeLowerBound()=%s (%s)\n",
autoTimeLowerBound, autoTimeLowerBound.toEpochMilli());
String priorities =
Arrays.stream(mEnvironment.autoOriginPriorities())
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 4a1d9c40a11c..23f6bbe48172 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -86,11 +86,9 @@ public final class ServiceConfigAccessor {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
}));
- // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
- private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
- // TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
- Duration.ofSeconds(20);
+ Duration.ofMinutes(1);
private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
private static final Object SLOCK = new Object();
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 91e172c9d153..b1ae626bf78d 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -7,6 +7,14 @@
"include-filter": "com.android.server.timezonedetector."
}
]
+ },
+ {
+ "name": "CtsTimeTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index f054c5756e20..59df6ff3582e 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -67,7 +67,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
@Override
public void onProviderUnbound() {
- handleProviderLost("onProviderUnbound()");
+ handleTemporaryFailure("onProviderUnbound()");
}
});
}
@@ -77,50 +77,6 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
mProxy.destroy();
}
- private void handleProviderLost(String reason) {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- ProviderState currentState = mCurrentState.get();
- switch (currentState.stateEnum) {
- case PROVIDER_STATE_STARTED_INITIALIZING:
- case PROVIDER_STATE_STARTED_UNCERTAIN:
- case PROVIDER_STATE_STARTED_CERTAIN: {
- // Losing a remote provider is treated as becoming uncertain.
- String msg = "handleProviderLost reason=" + reason
- + ", mProviderName=" + mProviderName
- + ", currentState=" + currentState;
- debugLog(msg);
- // This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
- // event == null
- ProviderState newState = currentState.newState(
- PROVIDER_STATE_STARTED_UNCERTAIN, null,
- currentState.currentUserConfiguration, msg);
- setCurrentState(newState, true);
- break;
- }
- case PROVIDER_STATE_STOPPED: {
- debugLog("handleProviderLost reason=" + reason
- + ", mProviderName=" + mProviderName
- + ", currentState=" + currentState
- + ": No state change required, provider is stopped.");
- break;
- }
- case PROVIDER_STATE_PERM_FAILED:
- case PROVIDER_STATE_DESTROYED: {
- debugLog("handleProviderLost reason=" + reason
- + ", mProviderName=" + mProviderName
- + ", currentState=" + currentState
- + ": No state change required, provider is terminated.");
- break;
- }
- default: {
- throw new IllegalStateException("Unknown currentState=" + currentState);
- }
- }
- }
- }
-
private void handleOnProviderBound() {
mThreadingDomain.assertCurrentThread();
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index d2190fdc5bea..b1019f3fa429 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -166,12 +166,6 @@ class ControllerImpl extends LocationTimeZoneProviderController {
stopProviders();
mPrimaryProvider.destroy();
mSecondaryProvider.destroy();
-
- // If the controller has made a "certain" suggestion, it should make an uncertain
- // suggestion to cancel it.
- if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- makeSuggestion(createUncertainSuggestion("Controller is destroyed"));
- }
}
}
@@ -182,6 +176,16 @@ class ControllerImpl extends LocationTimeZoneProviderController {
// By definition, if both providers are stopped, the controller is uncertain.
cancelUncertaintyTimeout();
+
+ // If a previous "certain" suggestion has been made, then a new "uncertain"
+ // suggestion must now be made to indicate the controller {does not / no longer has}
+ // an opinion and will not be sending further updates (until at least the providers are
+ // re-started).
+ if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
+ GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+ "Providers are stopping");
+ makeSuggestion(suggestion);
+ }
}
@GuardedBy("mSharedLock")
@@ -275,21 +279,6 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
} else {
stopProviders();
-
- // There can be an uncertainty timeout set if the controller most recently received
- // an uncertain event. This is a no-op if there isn't a timeout set.
- cancelUncertaintyTimeout();
-
- // If a previous "certain" suggestion has been made, then a new "uncertain"
- // suggestion must now be made to indicate the controller {does not / no longer has}
- // an opinion and will not be sending further updates (until at least the config
- // changes again and providers are re-started).
- if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- "Provider is stopped:"
- + " primary=" + mPrimaryProvider.getCurrentState());
- makeSuggestion(suggestion);
- }
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index e116a8742208..c2add2d85d26 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -592,9 +592,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
PROVIDER_STATE_STOPPED, null, null, "stopUpdates() called");
setCurrentState(newState, false);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
onStopUpdates();
}
@@ -656,9 +654,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
ProviderState newState = currentState.newState(
PROVIDER_STATE_PERM_FAILED, null, null, msg);
setCurrentState(newState, true);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
return;
}
case EVENT_TYPE_SUGGESTION:
@@ -691,9 +687,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
ProviderState newState = currentState.newState(
PROVIDER_STATE_PERM_FAILED, null, null, msg);
setCurrentState(newState, true);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
return;
}
@@ -709,9 +703,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
timeZoneProviderEvent, currentState.currentUserConfiguration,
"handleTimeZoneProviderEvent() when started");
setCurrentState(newState, true);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
return;
}
default: {
@@ -727,6 +719,52 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ /** For subclasses to invoke when needing to report a temporary failure. */
+ final void handleTemporaryFailure(String reason) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ ProviderState currentState = mCurrentState.get();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_STARTED_INITIALIZING:
+ case PROVIDER_STATE_STARTED_UNCERTAIN:
+ case PROVIDER_STATE_STARTED_CERTAIN: {
+ // A temporary failure is treated as becoming uncertain.
+ String msg = "handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState;
+ debugLog(msg);
+ // This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
+ // event == null
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_STARTED_UNCERTAIN, null,
+ currentState.currentUserConfiguration, msg);
+ setCurrentState(newState, true);
+ cancelInitializationTimeoutIfSet();
+ break;
+ }
+ case PROVIDER_STATE_STOPPED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is stopped.");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED:
+ case PROVIDER_STATE_DESTROYED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is terminated.");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown currentState=" + currentState);
+ }
+ }
+ }
+ }
+
@GuardedBy("mSharedLock")
private void assertIsStarted() {
ProviderState currentState = mCurrentState.get();
@@ -751,6 +789,13 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ @GuardedBy("mSharedLock")
+ private void cancelInitializationTimeoutIfSet() {
+ if (mInitializationTimeoutQueue.hasQueued()) {
+ mInitializationTimeoutQueue.cancel();
+ }
+ }
+
@VisibleForTesting
Duration getInitializationTimeoutDelay() {
synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 5723e1dcceb5..ee30fa2ac928 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -50,8 +50,6 @@ public final class ClientProfile {
*/
private final int mProcessId;
- private boolean mIsForeground;
-
/**
* All the clients that share the same resource would be under the same group id.
*
@@ -90,6 +88,12 @@ public final class ClientProfile {
private int mUsingCiCamId = INVALID_RESOURCE_ID;
/**
+ * If the priority is overwritten through
+ * {@link TunerResourceManagerService#setPriority(int, int)}.
+ */
+ private boolean mIsPriorityOverwritten = false;
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -121,17 +125,10 @@ public final class ClientProfile {
}
/**
- * Set the current isForeground status.
- */
- public void setForeground(boolean isForeground) {
- mIsForeground = isForeground;
- }
-
- /**
- * Get the previous recorded isForeground status.
+ * If the client priority is overwrttien.
*/
- public boolean isForeground() {
- return mIsForeground;
+ public boolean isPriorityOverwritten() {
+ return mIsPriorityOverwritten;
}
public int getGroupId() {
@@ -153,6 +150,17 @@ public final class ClientProfile {
mPriority = priority;
}
+ /**
+ * Overwrite the client priority.
+ */
+ public void overwritePriority(int priority) {
+ if (priority < 0) {
+ return;
+ }
+ mIsPriorityOverwritten = true;
+ mPriority = priority;
+ }
+
public void setNiceValue(int niceValue) {
mNiceValue = niceValue;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 988582da53ea..0c04b075485a 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -507,9 +507,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
.useCase(profile.useCase)
.processId(pid)
.build();
- clientProfile.setForeground(checkIsForeground(pid));
clientProfile.setPriority(
- getClientPriority(profile.useCase, clientProfile.isForeground()));
+ getClientPriority(profile.useCase, checkIsForeground(pid)));
addClientProfile(clientId[0], clientProfile, listener);
}
@@ -547,8 +546,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return false;
}
- profile.setForeground(checkIsForeground(profile.getProcessId()));
- profile.setPriority(priority);
+ profile.overwritePriority(priority);
profile.setNiceValue(niceValue);
return true;
@@ -694,7 +692,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
} else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
// Record the frontend id with the lowest client priority among all the
// in use frontends when no available frontend has been found.
- int priority = getOwnerClientPriority(fr.getOwnerClientId());
+ int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityFrHandle = fr.getHandle();
currentLowestPriority = priority;
@@ -760,7 +758,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
} else {
// Record the lnb id with the lowest client priority among all the
// in use lnb when no available lnb has been found.
- int priority = getOwnerClientPriority(lnb.getOwnerClientId());
+ int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityLnbHandle = lnb.getHandle();
currentLowestPriority = priority;
@@ -818,7 +816,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
for (int ownerId : cas.getOwnerClientIds()) {
// Record the client id with lowest priority that is using the current Cas system.
- int priority = getOwnerClientPriority(ownerId);
+ int priority = updateAndGetOwnerClientPriority(ownerId);
if (currentLowestPriority > priority) {
lowestPriorityOwnerId = ownerId;
currentLowestPriority = priority;
@@ -867,7 +865,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
for (int ownerId : ciCam.getOwnerClientIds()) {
// Record the client id with lowest priority that is using the current Cas system.
- int priority = getOwnerClientPriority(ownerId);
+ int priority = updateAndGetOwnerClientPriority(ownerId);
if (currentLowestPriority > priority) {
lowestPriorityOwnerId = ownerId;
currentLowestPriority = priority;
@@ -966,18 +964,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
- // This mothod is to sync up the request client's foreground/background status and update
- // the client priority accordingly whenever new resource request comes in.
- protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) {
- int pid = requestProfile.getProcessId();
- boolean currentIsForeground = checkIsForeground(pid);
- if (requestProfile.isForeground() == currentIsForeground) {
+ // This mothod is to sync up the request/holder client's foreground/background status and update
+ // the client priority accordingly whenever a new resource request comes in.
+ protected void clientPriorityUpdateOnRequest(ClientProfile profile) {
+ if (profile.isPriorityOverwritten()) {
// To avoid overriding the priority set through updateClientPriority API.
return;
}
- requestProfile.setForeground(currentIsForeground);
- requestProfile.setPriority(
- getClientPriority(requestProfile.getUseCase(), currentIsForeground));
+ int pid = profile.getProcessId();
+ boolean currentIsForeground = checkIsForeground(pid);
+ profile.setPriority(
+ getClientPriority(profile.getUseCase(), currentIsForeground));
}
@VisibleForTesting
@@ -1154,13 +1151,15 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
/**
- * Get the owner client's priority.
+ * Update and get the owner client's priority.
*
* @param clientId the owner client id.
* @return the priority of the owner client.
*/
- private int getOwnerClientPriority(int clientId) {
- return getClientProfile(clientId).getPriority();
+ private int updateAndGetOwnerClientPriority(int clientId) {
+ ClientProfile profile = getClientProfile(clientId);
+ clientPriorityUpdateOnRequest(profile);
+ return profile.getPriority();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index cc41f617ab8c..62afcc86792c 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -1,7 +1,7 @@
per-file Snappable.java = file:/services/core/java/com/android/server/pm/OWNERS
per-file Snappable.java = shombert@google.com
-per-file SnapShot* = file:/services/core/java/com/android/server/pm/OWNERS
-per-file SnapShot* = shombert@google.com
+per-file Snapshot* = file:/services/core/java/com/android/server/pm/OWNERS
+per-file Snapshot* = shombert@google.com
per-file Watchable* = file:/services/core/java/com/android/server/pm/OWNERS
per-file Watchable* = shombert@google.com
per-file Watched* = file:/services/core/java/com/android/server/pm/OWNERS
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 771332071756..2c563591b607 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1816,26 +1816,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
@Override
public void onUnlockUser(final int userId) {
- TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
- t.traceBegin("on-unlock-user-" + userId);
- try {
- synchronized (mLock) {
- if (mCurrentUserId == userId) {
- if (mWaitingForUnlock) {
- // the desired wallpaper is not direct-boot aware, load it now
- final WallpaperData systemWallpaper =
- getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- switchWallpaper(systemWallpaper, null);
- notifyCallbacksLocked(systemWallpaper);
- }
-
- // Make sure that the SELinux labeling of all the relevant files is correct.
- // This corrects for mislabeling bugs that might have arisen from move-to
- // operations involving the wallpaper files. This isn't timing-critical,
- // so we do it in the background to avoid holding up the user unlock operation.
- if (!mUserRestorecon.get(userId)) {
- mUserRestorecon.put(userId, true);
- Runnable relabeler = () -> {
+ synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ if (mWaitingForUnlock) {
+ // the desired wallpaper is not direct-boot aware, load it now
+ final WallpaperData systemWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ switchWallpaper(systemWallpaper, null);
+ notifyCallbacksLocked(systemWallpaper);
+ }
+
+ // Make sure that the SELinux labeling of all the relevant files is correct.
+ // This corrects for mislabeling bugs that might have arisen from move-to
+ // operations involving the wallpaper files. This isn't timing-critical,
+ // so we do it in the background to avoid holding up the user unlock operation.
+ if (!mUserRestorecon.get(userId)) {
+ mUserRestorecon.put(userId, true);
+ Runnable relabeler = () -> {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("Wallpaper_selinux_restorecon-" + userId);
+ try {
final File wallpaperDir = getWallpaperDir(userId);
for (String filename : sPerUserFiles) {
File f = new File(wallpaperDir, filename);
@@ -1843,13 +1843,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
SELinux.restorecon(f);
}
}
- };
- BackgroundThread.getHandler().post(relabeler);
- }
+ } finally {
+ t.traceEnd();
+ }
+ };
+ BackgroundThread.getHandler().post(relabeler);
}
}
- } finally {
- t.traceEnd();
}
}
@@ -1868,7 +1868,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
void switchUser(int userId, IRemoteCallback reply) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
- t.traceBegin("switch-user-" + userId);
+ t.traceBegin("Wallpaper_switch-user-" + userId);
try {
final WallpaperData systemWallpaper;
final WallpaperData lockWallpaper;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a24319f7a98c..54d97eea1521 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -34,6 +36,7 @@ import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_P
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.LOGGING_TYPE;
import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
@@ -42,6 +45,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
+import android.accessibilityservice.AccessibilityTrace;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -90,6 +94,7 @@ import android.view.animation.Interpolator;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
@@ -99,8 +104,6 @@ import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallba
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -116,21 +119,16 @@ final class AccessibilityController {
private static final String TAG = AccessibilityController.class.getSimpleName();
private static final Object STATIC_LOCK = new Object();
- static AccessibilityControllerInternal
+ static AccessibilityControllerInternalImpl
getAccessibilityControllerInternal(WindowManagerService service) {
return AccessibilityControllerInternalImpl.getInstance(service);
}
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowManagerService mService;
private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
- AccessibilityController(WindowManagerService service) {
- mService = service;
- mAccessibilityTracing = AccessibilityTracing.getInstance(service);
- }
-
private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
@@ -138,10 +136,17 @@ final class AccessibilityController {
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
+ AccessibilityController(WindowManagerService service) {
+ mService = service;
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(service);
+ }
+
boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".setMagnificationCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; callbacks={" + callbacks + "}");
}
boolean result = false;
@@ -172,25 +177,31 @@ final class AccessibilityController {
/**
* Sets a callback for observing which windows are touchable for the purposes
- * of accessibility on specified display.
+ * of accessibility on specified display. When a display is reparented and becomes
+ * an embedded one, the {@link WindowsForAccessibilityCallback#onDisplayReparented(int)}
+ * will notify the accessibility framework to remove the un-used window observer of
+ * this embedded display.
*
* @param displayId The logical display id.
* @param callback The callback.
- * @return {@code false} if display id is not valid or an embedded display.
+ * @return {@code false} if display id is not valid or an embedded display when the callback
+ * isn't null.
*/
boolean setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".setWindowsForAccessibilityCallback",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; callback={" + callback + "}");
}
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
- if (dc == null) {
- return false;
- }
if (callback != null) {
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ return false;
+ }
+
WindowsForAccessibilityObserver observer =
mWindowsForAccessibilityObserver.get(displayId);
if (isEmbeddedDisplay(dc)) {
@@ -209,21 +220,13 @@ final class AccessibilityController {
if (Build.IS_DEBUGGABLE) {
throw new IllegalStateException(errorMessage);
}
- removeObserverOfEmbeddedDisplay(observer);
+ removeObserversForEmbeddedChildDisplays(observer);
mWindowsForAccessibilityObserver.remove(displayId);
}
observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
mWindowsForAccessibilityObserver.put(displayId, observer);
mAllObserversInitialized &= observer.mInitialized;
} else {
- if (isEmbeddedDisplay(dc)) {
- // If this display is an embedded one, its window observer should be removed along
- // with the window observer of its parent display removed because the window
- // observer of the embedded display and its parent display is the same, and would
- // be removed together when stopping the window tracking of its parent display. So
- // here don't need to do removing window observer of the embedded display again.
- return true;
- }
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver == null) {
@@ -234,16 +237,17 @@ final class AccessibilityController {
throw new IllegalStateException(errorMessage);
}
}
- removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
+ removeObserversForEmbeddedChildDisplays(windowsForA11yObserver);
mWindowsForAccessibilityObserver.remove(displayId);
}
return true;
}
void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".performComputeChangedWindowsNot",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; forceSend=" + forceSend);
}
WindowsForAccessibilityObserver observer = null;
@@ -260,8 +264,10 @@ final class AccessibilityController {
}
void setMagnificationSpec(int displayId, MagnificationSpec spec) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".setMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; spec={" + spec + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -276,8 +282,9 @@ final class AccessibilityController {
}
void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationRegion",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
+ "}");
}
@@ -288,9 +295,10 @@ final class AccessibilityController {
}
void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; rectangle={" + rectangle + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -301,9 +309,11 @@ final class AccessibilityController {
}
void onWindowLayersChanged(int displayId) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ "displayId=" + displayId);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
@@ -316,15 +326,18 @@ final class AccessibilityController {
}
}
- void onRotationChanged(DisplayContent displayContent) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+ void onDisplaySizeChanged(DisplayContent displayContent) {
+
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onRotationChanged",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayContent={" + displayContent + "}");
}
final int displayId = displayContent.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onRotationChanged(displayContent);
+ displayMagnifier.onDisplaySizeChanged(displayContent);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
@@ -334,8 +347,9 @@ final class AccessibilityController {
}
void onAppWindowTransition(int displayId, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onAppWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transition=" + transition);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -346,8 +360,10 @@ final class AccessibilityController {
}
void onWindowTransition(WindowState windowState, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"windowState={" + windowState + "}; transition=" + transition);
}
final int displayId = windowState.getDisplayId();
@@ -364,9 +380,9 @@ final class AccessibilityController {
void onWindowFocusChangedNot(int displayId) {
// Not relevant for the display magnifier.
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowFocusChangedNot",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId);
}
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
@@ -426,12 +442,10 @@ final class AccessibilityController {
}
void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onSomeWindowResizedOrMoved",
- "displayIds={" + displayIds.toString() + "}",
- "".getBytes(),
- callingUid);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid);
}
// Not relevant for the display magnifier.
for (int i = 0; i < displayIds.length; i++) {
@@ -444,9 +458,10 @@ final class AccessibilityController {
}
void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transaction={" + t + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -457,8 +472,9 @@ final class AccessibilityController {
}
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowState={" + windowState + "}");
}
final int displayId = windowState.getDisplayId();
@@ -470,17 +486,19 @@ final class AccessibilityController {
}
boolean hasCallbacks() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".hasCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
}
return (mDisplayMagnifiers.size() > 0
|| mWindowsForAccessibilityObserver.size() > 0);
}
void setForceShowMagnifiableBounds(int displayId, boolean show) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
- "displayId=" + displayId + "; show=" + show);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
@@ -497,39 +515,50 @@ final class AccessibilityController {
void handleWindowObserverOfEmbeddedDisplay(
int embeddedDisplayId, WindowState parentWindow, int callingUid) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
- + parentWindow + "}",
- "".getBytes(),
- callingUid);
+ + parentWindow + "}", "".getBytes(), callingUid);
}
if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
return;
}
- // Finds the parent display of this embedded display
- final int parentDisplayId;
- WindowState candidate = parentWindow;
- while (candidate != null) {
- parentWindow = candidate;
- candidate = parentWindow.getDisplayContent().getParentWindow();
+ mService.mH.sendMessage(PooledLambda.obtainMessage(
+ AccessibilityController::updateWindowObserverOfEmbeddedDisplay,
+ this, embeddedDisplayId, parentWindow));
+ }
+
+ private void updateWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
+ WindowState parentWindow) {
+ final WindowsForAccessibilityObserver windowsForA11yObserver;
+
+ synchronized (mService.mGlobalLock) {
+ // Finds the parent display of this embedded display
+ WindowState candidate = parentWindow;
+ while (candidate != null) {
+ parentWindow = candidate;
+ candidate = parentWindow.getDisplayContent().getParentWindow();
+ }
+ final int parentDisplayId = parentWindow.getDisplayId();
+ // Uses the observer of parent display
+ windowsForA11yObserver = mWindowsForAccessibilityObserver.get(parentDisplayId);
}
- parentDisplayId = parentWindow.getDisplayId();
- // Uses the observer of parent display
- final WindowsForAccessibilityObserver windowsForA11yObserver =
- mWindowsForAccessibilityObserver.get(parentDisplayId);
if (windowsForA11yObserver != null) {
+ windowsForA11yObserver.notifyDisplayReparented(embeddedDisplayId);
windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId);
- // Replaces the observer of embedded display to the one of parent display
- mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
+ synchronized (mService.mGlobalLock) {
+ // Replaces the observer of embedded display to the one of parent display
+ mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
+ }
}
}
void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
- "windowState=" + windowState + "; shown=" + shown);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onImeSurfaceShownChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "windowState=" + windowState + ";shown=" + shown);
}
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -555,7 +584,7 @@ final class AccessibilityController {
+ "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
}
- private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
+ private void removeObserversForEmbeddedChildDisplays(WindowsForAccessibilityObserver
observerOfParentDisplay) {
final IntArray embeddedDisplayIdList =
observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
@@ -580,7 +609,7 @@ final class AccessibilityController {
private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
- private static final boolean DEBUG_ROTATION = false;
+ private static final boolean DEBUG_DISPLAY_SIZE = false;
private static final boolean DEBUG_LAYERS = false;
private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
private static final boolean DEBUG_VIEWPORT_WINDOW = false;
@@ -599,7 +628,7 @@ final class AccessibilityController {
private final Handler mHandler;
private final DisplayContent mDisplayContent;
private final Display mDisplay;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final MagnificationCallbacks mCallbacks;
@@ -618,11 +647,13 @@ final class AccessibilityController {
mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
- mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowManagerService={" + windowManagerService + "}; displayContent={"
+ displayContent + "}; display={" + display + "}; callbacks={"
+ callbacks + "}");
@@ -630,9 +661,9 @@ final class AccessibilityController {
}
void setMagnificationSpec(MagnificationSpec spec) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}");
}
mMagnifedViewport.updateMagnificationSpec(spec);
mMagnifedViewport.recomputeBounds();
@@ -642,25 +673,26 @@ final class AccessibilityController {
}
void setForceShowMagnifiableBounds(boolean show) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK, "show=" + show);
}
mForceShowMagnifiableBounds = show;
mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
}
boolean isForceShowingMagnifiableBounds() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK);
}
return mForceShowMagnifiableBounds;
}
void onRectangleOnScreenRequested(Rect rectangle) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
}
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
@@ -683,8 +715,9 @@ final class AccessibilityController {
}
void onWindowLayersChanged() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
+ LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK);
}
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
@@ -693,23 +726,24 @@ final class AccessibilityController {
mService.scheduleAnimationLocked();
}
- void onRotationChanged(DisplayContent displayContent) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+ void onDisplaySizeChanged(DisplayContent displayContent) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayContent={" + displayContent + "}");
}
- if (DEBUG_ROTATION) {
+ if (DEBUG_DISPLAY_SIZE) {
final int rotation = displayContent.getRotation();
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChanged();
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+ mMagnifedViewport.onDisplaySizeChanged();
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED);
}
void onAppWindowTransition(int displayId, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onAppWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transition=" + transition);
}
if (DEBUG_WINDOW_TRANSITIONS) {
@@ -733,8 +767,9 @@ final class AccessibilityController {
}
void onWindowTransition(WindowState windowState, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowState={" + windowState + "}; transition=" + transition);
}
if (DEBUG_WINDOW_TRANSITIONS) {
@@ -791,18 +826,18 @@ final class AccessibilityController {
}
void onImeSurfaceShownChanged(boolean shown) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onImeSurfaceShownChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "shown=" + shown);
}
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
shown ? 1 : 0, 0).sendToTarget();
}
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
- "windowState={" + windowState + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpecForWindow",
+ FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}");
}
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
if (spec != null && !spec.isNop()) {
@@ -814,8 +849,9 @@ final class AccessibilityController {
}
void getMagnificationRegion(Region outMagnificationRegion) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
+ FLAGS_MAGNIFICATION_CALLBACK,
"outMagnificationRegion={" + outMagnificationRegion + "}");
}
// Make sure we're working with the most current bounds
@@ -824,25 +860,26 @@ final class AccessibilityController {
}
void destroy() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
}
mMagnifedViewport.destroyWindow();
}
// Can be called outside of a surface transaction
void showMagnificationBoundsIfNeeded() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK);
}
mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
.sendToTarget();
}
void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
- "transition={" + t + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
}
mMagnifedViewport.drawWindowIfNeeded(t);
}
@@ -887,7 +924,8 @@ final class AccessibilityController {
if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
- mDisplay.getRealSize(mScreenSize);
+
+ getDisplaySizeLocked(mScreenSize);
final int centerXY = mScreenSize.x / 2;
mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
} else {
@@ -917,7 +955,7 @@ final class AccessibilityController {
}
void recomputeBounds() {
- mDisplay.getRealSize(mScreenSize);
+ getDisplaySizeLocked(mScreenSize);
final int screenWidth = mScreenSize.x;
final int screenHeight = mScreenSize.y;
@@ -1052,9 +1090,10 @@ final class AccessibilityController {
|| windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
}
- void onRotationChanged() {
+ void onDisplaySizeChanged() {
// If we are showing the magnification border, hide it immediately so
- // the user does not see strange artifacts during rotation. The screenshot
+ // the user does not see strange artifacts during display size changed caused by
+ // rotation or folding/unfolding the device. In the rotation case, the screenshot
// used for rotation already has the border. After the rotation is complete
// we will show the border.
if (isMagnifying() || isForceShowingMagnifiableBounds()) {
@@ -1112,6 +1151,12 @@ final class AccessibilityController {
}, false /* traverseTopToBottom */ );
}
+ private void getDisplaySizeLocked(Point outSize) {
+ final Rect bounds =
+ mDisplayContent.getConfiguration().windowConfiguration.getBounds();
+ outSize.set(bounds.width(), bounds.height());
+ }
+
void dump(PrintWriter pw, String prefix) {
mWindow.dump(pw, prefix);
}
@@ -1226,7 +1271,7 @@ final class AccessibilityController {
void updateSize() {
synchronized (mService.mGlobalLock) {
- mDisplay.getRealSize(mScreenSize);
+ getDisplaySizeLocked(mScreenSize);
mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
PixelFormat.RGBA_8888);
invalidate(mDirtyRect);
@@ -1365,7 +1410,7 @@ final class AccessibilityController {
public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
- public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+ public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
@@ -1397,9 +1442,8 @@ final class AccessibilityController {
mCallbacks.onUserContextChanged();
} break;
- case MESSAGE_NOTIFY_ROTATION_CHANGED: {
- final int rotation = message.arg1;
- mCallbacks.onRotationChanged(rotation);
+ case MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED: {
+ mCallbacks.onDisplaySizeChanged();
} break;
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
@@ -1482,7 +1526,7 @@ final class AccessibilityController {
private final Handler mHandler;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowsForAccessibilityCallback mCallback;
@@ -1502,24 +1546,26 @@ final class AccessibilityController {
mCallback = callback;
mDisplayId = displayId;
mHandler = new MyHandler(mService.mH.getLooper());
- mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(mService);
mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
.getSendRecurringAccessibilityEventsInterval();
computeChangedWindows(true);
}
void performComputeChangedWindows(boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
- "forceSend=" + forceSend);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".performComputeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
}
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows(forceSend);
}
void scheduleComputeChangedWindows() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".scheduleComputeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
}
if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
@@ -1542,6 +1588,13 @@ final class AccessibilityController {
mEmbeddedDisplayIdList.add(displayId);
}
+ void notifyDisplayReparented(int embeddedDisplayId) {
+ // Notifies the A11y framework the display is reparented and
+ // becomes an embedded display for removing the un-used
+ // displayWindowObserver of this embedded one.
+ mCallback.onDisplayReparented(embeddedDisplayId);
+ }
+
boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
@@ -1594,9 +1647,9 @@ final class AccessibilityController {
* @param forceSend Send the windows the accessibility even if they haven't changed.
*/
void computeChangedWindows(boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".computeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
}
if (DEBUG) {
Slog.i(LOG_TAG, "computeChangedWindows()");
@@ -1945,8 +1998,8 @@ final class AccessibilityController {
private static final class AccessibilityControllerInternalImpl
implements AccessibilityControllerInternal {
- private static AccessibilityControllerInternal sInstance;
- static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+ private static AccessibilityControllerInternalImpl sInstance;
+ static AccessibilityControllerInternalImpl getInstance(WindowManagerService service) {
synchronized (STATIC_LOCK) {
if (sInstance == null) {
sInstance = new AccessibilityControllerInternalImpl(service);
@@ -1956,18 +2009,23 @@ final class AccessibilityController {
}
private final AccessibilityTracing mTracing;
+ private volatile long mEnabledTracingFlags;
+
private AccessibilityControllerInternalImpl(WindowManagerService service) {
mTracing = AccessibilityTracing.getInstance(service);
+ mEnabledTracingFlags = 0L;
}
@Override
- public void startTrace() {
+ public void startTrace(long loggingTypes) {
+ mEnabledTracingFlags = loggingTypes;
mTracing.startTrace();
}
@Override
public void stopTrace() {
mTracing.stopTrace();
+ mEnabledTracingFlags = 0L;
}
@Override
@@ -1975,19 +2033,37 @@ final class AccessibilityController {
return mTracing.isEnabled();
}
+ boolean isTracingEnabled(long flags) {
+ return (flags & mEnabledTracingFlags) != 0L;
+ }
+
+ void logTrace(String where, long loggingTypes) {
+ logTrace(where, loggingTypes, "");
+ }
+
+ void logTrace(String where, long loggingTypes, String callingParams) {
+ logTrace(where, loggingTypes, callingParams, "".getBytes(), Binder.getCallingUid());
+ }
+
+ void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid,
+ new HashSet<String>(Arrays.asList("logTrace")));
+ }
+
@Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+ public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
+ ignoreStackEntries);
}
@Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callStack, long timeStamp, int processId, long threadId) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, callStack, timeStamp,
- processId, threadId);
+ public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callStack, long timeStamp, int processId,
+ long threadId, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack,
+ timeStamp, processId, threadId, ignoreStackEntries);
}
}
@@ -2004,7 +2080,6 @@ final class AccessibilityController {
private static final int BUFFER_CAPACITY = 1024 * 1024 * 12;
private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
- private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
private static final String TAG = "AccessibilityTracing";
private static final long MAGIC_NUMBER_VALUE =
((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
@@ -2034,13 +2109,6 @@ final class AccessibilityController {
return;
}
synchronized (mLock) {
- try {
- Files.createDirectories(Paths.get(TRACE_DIRECTORY));
- mTraceFile.createNewFile();
- } catch (Exception e) {
- Slog.e(TAG, "Error: Failed to create trace file.");
- return;
- }
mEnabled = true;
mBuffer.resetBuffer();
}
@@ -2071,106 +2139,150 @@ final class AccessibilityController {
/**
* Write an accessibility trace log entry.
*/
- void logState(String where) {
+ void logState(String where, long loggingTypes) {
if (!mEnabled) {
return;
}
- logState(where, "");
+ logState(where, loggingTypes, "");
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams) {
+ void logState(String where, long loggingTypes, String callingParams) {
if (!mEnabled) {
return;
}
- logState(where, callingParams, "".getBytes());
+ logState(where, loggingTypes, callingParams, "".getBytes());
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump) {
if (!mEnabled) {
return;
}
- logState(where, callingParams, a11yDump, Binder.getCallingUid());
+ logState(where, loggingTypes, callingParams, a11yDump, Binder.getCallingUid(),
+ new HashSet<String>(Arrays.asList("logState")));
}
/**
* Write an accessibility trace log entry.
*/
- void logState(
- String where, String callingParams, byte[] a11yDump, int callingUid) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
-
- logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+ ignoreStackEntries.add("logState");
+ logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTraceElements,
+ ignoreStackEntries);
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
-
- log(where, callingParams, a11yDump, callingUid, stackTrace,
+ log(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
SystemClock.elapsedRealtimeNanos(),
Process.myPid() + ":" + Application.getProcessName(),
- Thread.currentThread().getId() + ":" + Thread.currentThread().getName());
+ Thread.currentThread().getId() + ":" + Thread.currentThread().getName(),
+ ignoreStackEntries);
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callingStack, long timeStamp, int processId, long threadId) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callingStack, long timeStamp, int processId,
+ long threadId, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
- log(where, callingParams, a11yDump, callingUid, callingStack, timeStamp,
- String.valueOf(processId), String.valueOf(threadId));
+ log(where, loggingTypes, callingParams, a11yDump, callingUid, callingStack, timeStamp,
+ String.valueOf(processId), String.valueOf(threadId), ignoreStackEntries);
}
- private String toStackTraceString(StackTraceElement[] stackTraceElements) {
+ private String toStackTraceString(StackTraceElement[] stackTraceElements,
+ Set<String> ignoreStackEntries) {
+
if (stackTraceElements == null) {
return "";
}
+
StringBuilder stringBuilder = new StringBuilder();
- boolean skip = true;
- for (int i = 0; i < stackTraceElements.length; i++) {
- if (stackTraceElements[i].toString().contains(
- AccessibilityTracing.class.getSimpleName())) {
- skip = false;
- } else if (!skip) {
- stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ int i = 0;
+
+ // Skip the first a few elements until after any ignoreStackEntries
+ int firstMatch = -1;
+ while (i < stackTraceElements.length) {
+ for (String ele : ignoreStackEntries) {
+ if (stackTraceElements[i].toString().contains(ele)) {
+ // found the first stack element containing the ignorable stack entries
+ firstMatch = i;
+ break;
+ }
+ }
+ if (firstMatch < 0) {
+ // Haven't found the first match yet, continue
+ i++;
+ } else {
+ break;
+ }
+ }
+ int lastMatch = firstMatch;
+ if (i < stackTraceElements.length) {
+ i++;
+ // Found the first match. Now look for the last match.
+ while (i < stackTraceElements.length) {
+ for (String ele : ignoreStackEntries) {
+ if (stackTraceElements[i].toString().contains(ele)) {
+ // This is a match. Look at the next stack element.
+ lastMatch = i;
+ break;
+ }
+ }
+ if (lastMatch != i) {
+ // Found a no-match.
+ break;
+ }
+ i++;
}
}
+
+ i = lastMatch + 1;
+ while (i < stackTraceElements.length) {
+ stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ i++;
+ }
return stringBuilder.toString();
}
/**
* Write the current state to the buffer
*/
- private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callingStack, long timeStamp, String processName,
- String threadName) {
+ private void log(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callingStack, long timeStamp,
+ String processName, String threadName, Set<String> ignoreStackEntries) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = timeStamp;
- args.arg2 = where;
- args.arg3 = processName;
- args.arg4 = threadName;
- args.arg5 = callingUid;
- args.arg6 = callingParams;
- args.arg7 = callingStack;
- args.arg8 = a11yDump;
- mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+ args.arg2 = loggingTypes;
+ args.arg3 = where;
+ args.arg4 = processName;
+ args.arg5 = threadName;
+ args.arg6 = ignoreStackEntries;
+ args.arg7 = callingParams;
+ args.arg8 = callingStack;
+ args.arg9 = a11yDump;
+
+ mHandler.obtainMessage(
+ LogHandler.MESSAGE_LOG_TRACE_ENTRY, callingUid, 0, args).sendToTarget();
}
/**
@@ -2199,8 +2311,6 @@ final class AccessibilityController {
LocalServices.getService(PackageManagerInternal.class);
long tokenOuter = os.start(ENTRY);
- String callingStack =
- toStackTraceString((StackTraceElement[]) args.arg7);
long reportedTimeStampNanos = (long) args.arg1;
long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -2213,13 +2323,25 @@ final class AccessibilityController {
os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos);
os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString());
- os.write(WHERE, (String) args.arg2);
- os.write(PROCESS_NAME, (String) args.arg3);
- os.write(THREAD_ID_NAME, (String) args.arg4);
- os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg5));
- os.write(CALLING_PARAMS, (String) args.arg6);
+
+ long loggingTypes = (long) args.arg2;
+ List<String> loggingTypeNames =
+ AccessibilityTrace.getNamesOfLoggingTypes(loggingTypes);
+
+ for (String type : loggingTypeNames) {
+ os.write(LOGGING_TYPE, type);
+ }
+ os.write(WHERE, (String) args.arg3);
+ os.write(PROCESS_NAME, (String) args.arg4);
+ os.write(THREAD_ID_NAME, (String) args.arg5);
+ os.write(CALLING_PKG, pmInternal.getNameForUid(message.arg1));
+ os.write(CALLING_PARAMS, (String) args.arg7);
+
+ String callingStack = toStackTraceString(
+ (StackTraceElement[]) args.arg8, (Set<String>) args.arg6);
+
os.write(CALLING_STACKS, callingStack);
- os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg8);
+ os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 3a4faf73bfe1..8a76e3e586e0 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -28,6 +28,11 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -35,8 +40,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
import android.annotation.NonNull;
import android.app.Activity;
@@ -68,6 +71,7 @@ import android.service.voice.VoiceInteractionManagerInternal;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
import android.window.SizeConfigurationBuckets;
+import android.window.TransitionInfo;
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -188,7 +192,7 @@ class ActivityClientController extends IActivityClientController.Stub {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
r = ActivityRecord.isInRootTaskLocked(token);
if (r != null) {
- if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
// The activity was requested to restart from
// {@link #restartActivityProcessIfVisible}.
restartingName = r.app.mName;
@@ -1010,10 +1014,13 @@ class ActivityClientController extends IActivityClientController.Stub {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
+ if (r != null && r.isState(RESUMED, PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
packageName, enterAnim, exitAnim, null, null,
r.mOverrideTaskTransition);
+ mService.getTransitionController().setOverrideAnimation(
+ TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
+ enterAnim, exitAnim, r.mOverrideTaskTransition));
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0f6a71823334..b2e3fcbdccfb 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -775,7 +775,7 @@ class ActivityMetricsLogger {
Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested
+ " state=" + r.getState() + " finishing=" + r.finishing);
}
- if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) {
+ if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) {
// The activity may be launching while keyguard is locked. The keyguard may be dismissed
// after the activity finished relayout, so skip the visibility check to avoid aborting
// the tracking of launch event.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 44682525edd2..707b369f03f5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -129,6 +129,17 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
@@ -191,18 +202,7 @@ import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESTARTING_PROCESS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -302,6 +302,7 @@ import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
@@ -315,12 +316,14 @@ import android.window.SplashScreen;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
+import android.window.TransitionInfo.AnimationOptions;
import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
@@ -337,7 +340,6 @@ import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -346,6 +348,7 @@ import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -489,7 +492,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
UriPermissionOwner uriPermissions; // current special URI access perms.
WindowProcessController app; // if non-null, hosting application
- private ActivityState mState; // current state we are in
+ private State mState; // current state we are in
private Bundle mIcicle; // last saved activity state
private PersistableBundle mPersistentState; // last persistently saved activity state
private boolean mHaveState = true; // Indicates whether the last saved state of activity is
@@ -551,6 +554,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
static final int LAUNCH_SOURCE_TYPE_HOME = 2;
static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
static final int LAUNCH_SOURCE_TYPE_APPLICATION = 4;
+
+ enum State {
+ INITIALIZING,
+ STARTED,
+ RESUMED,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED,
+ RESTARTING_PROCESS
+ }
+
/**
* The type of launch source.
*/
@@ -647,6 +665,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean allDrawn;
private boolean mLastAllDrawn;
+ /**
+ * Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this
+ * Actiivty was part of a syncset, all windows were ready by the time the sync was ready (vs.
+ * only the top-occluding ones). The assumption here is if some were not ready, they were
+ * covered with starting-window/splash-screen.
+ */
+ boolean mLastAllReadyAtSync = false;
+
private boolean mLastContainsShowWhenLockedWindow;
private boolean mLastContainsDismissKeyguardWindow;
private boolean mLastContainsTurnScreenOnWindow;
@@ -681,6 +707,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private boolean mInSizeCompatModeForBounds = false;
// Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio().
+ // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration
+ // but that isn't reflected in this boolean.
private boolean mIsAspectRatioApplied = false;
// Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
@@ -790,6 +818,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpOutNonDecorBounds = new Rect();
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -1139,6 +1168,76 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLetterboxUiController.dump(pw, prefix);
}
+ static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
+ String prefix, String label, boolean complete, boolean brief, boolean client,
+ String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
+ if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+ return false;
+ }
+
+ final boolean full = !brief && (complete || !r.isInHistory());
+ if (needNL) {
+ pw.println("");
+ }
+ if (header != null) {
+ header.run();
+ }
+
+ String innerPrefix = prefix + " ";
+ String[] args = new String[0];
+ if (lastTask != r.getTask()) {
+ lastTask = r.getTask();
+ pw.print(prefix);
+ pw.print(full ? "* " : " ");
+ pw.println(lastTask);
+ if (full) {
+ lastTask.dump(pw, prefix + " ");
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ if (lastTask.intent != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(lastTask.intent.toInsecureString());
+ }
+ }
+ }
+ pw.print(prefix); pw.print(full ? "* " : " "); pw.print(label);
+ pw.print(" #"); pw.print(index); pw.print(": ");
+ pw.println(r);
+ if (full) {
+ r.dump(pw, innerPrefix, true /* dumpAll */);
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ pw.print(innerPrefix);
+ pw.println(r.intent.toInsecureString());
+ if (r.app != null) {
+ pw.print(innerPrefix);
+ pw.println(r.app);
+ }
+ }
+ if (client && r.attachedToProcess()) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.getThread().dumpActivity(
+ tp.getWriteFd(), r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
+ }
+ return true;
+ }
+
void setAppTimeTracker(AppTimeTracker att) {
appTimeTracker = att;
}
@@ -1248,11 +1347,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updatePictureInPictureMode(null, false);
} else {
mLastReportedMultiWindowMode = inMultiWindowMode;
- computeConfigurationAfterMultiWindowModeChange();
// If the activity is in stopping or stopped state, for instance, it's in the
// split screen task and not the top one, the last configuration it should keep
// is the one before multi-window mode change.
- final ActivityState state = getState();
+ final State state = getState();
if (state != STOPPED && state != STOPPING) {
ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
true /* ignoreVisibility */);
@@ -1275,31 +1373,25 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// precede the configuration change from the resize.
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
- if (targetRootTaskBounds != null && !targetRootTaskBounds.isEmpty()) {
- computeConfigurationAfterMultiWindowModeChange();
- }
ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
true /* ignoreVisibility */);
}
}
- private void computeConfigurationAfterMultiWindowModeChange() {
- final Configuration newConfig = new Configuration();
- newConfig.setTo(task.getRequestedOverrideConfiguration());
- Rect outBounds = newConfig.windowConfiguration.getBounds();
- final Configuration parentConfig = task.getParent().getConfiguration();
- task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
- task.computeConfigResourceOverrides(newConfig, parentConfig);
- }
-
Task getTask() {
return task;
}
+ @Nullable
+ TaskFragment getTaskFragment() {
+ WindowContainer parent = getParent();
+ return parent != null ? parent.asTaskFragment() : null;
+ }
+
@Override
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- final Task oldTask = oldParent != null ? (Task) oldParent : null;
- final Task newTask = newParent != null ? (Task) newParent : null;
+ final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null;
+ final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null;
this.task = newTask;
super.onParentChanged(newParent, oldParent);
@@ -1357,11 +1449,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updateColorTransform();
- if (oldTask != null) {
- oldTask.cleanUpActivityReferences(this);
+ if (oldParent != null) {
+ ((TaskFragment) oldParent).cleanUpActivityReferences(this);
}
- if (newTask != null && isState(RESUMED)) {
- newTask.setResumedActivity(this, "onParentChanged");
+
+ if (newParent != null && isState(RESUMED)) {
+ ((TaskFragment) newParent).setResumedActivity(this, "onParentChanged");
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -2325,23 +2418,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Reparents this activity into {@param newTask} at the provided {@param position}. The caller
- * should ensure that the {@param newTask} is not already the parent of this activity.
+ * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
+ * caller should ensure that the {@param newTaskFrag} is not already the parent of this
+ * activity.
*/
- void reparent(Task newTask, int position, String reason) {
+ void reparent(TaskFragment newTaskFrag, int position, String reason) {
if (getParent() == null) {
Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
return;
}
- final Task prevTask = task;
- if (prevTask == newTask) {
- throw new IllegalArgumentException(reason + ": task=" + newTask
+ final TaskFragment prevTaskFrag = getTaskFragment();
+ if (prevTaskFrag == newTaskFrag) {
+ throw new IllegalArgumentException(reason + ": task fragment =" + newTaskFrag
+ " is already the parent of r=" + this);
}
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s"
- + " to task=%d at %d", this, task.mTaskId, position);
- reparent(newTask, position);
+ + " to new task fragment in task=%d at %d", this, task.mTaskId, position);
+ reparent(newTaskFrag, position);
}
private boolean isHomeIntent(Intent intent) {
@@ -2907,7 +3001,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Task rootTask = getRootTask();
- final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getResumedActivity() == null)
+ final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null)
&& rootTask.isFocusedRootTaskOnDisplay()
// Do not adjust focus task because the task will be reused to launch new activity.
&& !task.isClearingToReuseTask();
@@ -2986,12 +3080,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Tell window manager to prepare for this one to be removed.
setVisibility(false);
- if (task.getPausingActivity() == null) {
+ if (getTaskFragment().getPausingActivity() == null) {
ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this);
if (DEBUG_USER_LEAVING) {
Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");
}
- task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
+ getTaskFragment().startPausing(false /* userLeaving */, false /* uiSleeping */,
null /* resuming */, "finish");
}
@@ -3122,8 +3216,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Clear last paused activity to ensure top activity can be resumed during sleeping.
if (isNextNotYetVisible && mDisplayContent.isSleeping()
- && next == next.getRootTask().mLastPausedActivity) {
- next.getRootTask().mLastPausedActivity = null;
+ && next == next.getTaskFragment().mLastPausedActivity) {
+ next.getTaskFragment().clearLastPausedActivity();
}
if (isCurrentVisible) {
@@ -3310,8 +3404,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_SWITCH) {
final Task task = getTask();
Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
- + " resumed=" + task.getResumedActivity()
- + " pausing=" + task.getPausingActivity()
+ + " resumed=" + task.getTopResumedActivity()
+ + " pausing=" + task.getTopPausingActivity()
+ " for reason " + reason);
}
return destroyImmediately(reason);
@@ -3390,7 +3484,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* Note: Call before {@link #removeFromHistory(String)}.
*/
void cleanUp(boolean cleanServices, boolean setState) {
- task.cleanUpActivityReferences(this);
+ getTaskFragment().cleanUpActivityReferences(this);
clearLastParentBeforePip();
// Clean up the splash screen if it was still displayed.
@@ -3498,7 +3592,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// failed more than twice. Skip activities that's already finishing cleanly by itself.
remove = false;
} else if ((!mHaveState && !stateNotNeeded
- && !isState(ActivityState.RESTARTING_PROCESS)) || finishing) {
+ && !isState(State.RESTARTING_PROCESS)) || finishing) {
// Don't currently have state for the activity, or it is finishing -- always remove it.
remove = true;
} else if (!mVisibleRequested && launchCount > 2
@@ -4222,6 +4316,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
final int animationType = pendingOptions.getAnimationType();
final DisplayContent displayContent = getDisplayContent();
+ AnimationOptions options = null;
switch (animationType) {
case ANIM_CUSTOM:
displayContent.mAppTransition.overridePendingAppTransition(
@@ -4231,11 +4326,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingOptions.getAnimationStartedListener(),
pendingOptions.getAnimationFinishedListener(),
pendingOptions.getOverrideTaskTransition());
+ options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
+ pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
+ pendingOptions.getOverrideTaskTransition());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
+ options = AnimationOptions.makeClipRevealAnimOptions(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4247,6 +4348,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
+ options = AnimationOptions.makeScaleUpAnimOptions(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4262,6 +4366,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getAnimationStartedListener(),
scaleUp);
+ options = AnimationOptions.makeThumnbnailAnimOptions(buffer,
+ pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
if (intent.getSourceBounds() == null && buffer != null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4301,6 +4407,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
case ANIM_OPEN_CROSS_PROFILE_APPS:
displayContent.mAppTransition
.overridePendingAppTransitionStartCrossProfileApps();
+ options = AnimationOptions.makeCrossProfileAnimOptions();
break;
case ANIM_NONE:
case ANIM_UNDEFINED:
@@ -4309,6 +4416,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
}
+
+ if (options != null) {
+ mAtmService.getTransitionController().setOverrideAnimation(options);
+ }
}
void clearAllDrawn() {
@@ -4441,6 +4552,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean getDeferHidingClient() {
+ return mDeferHidingClient;
+ }
+
@Override
boolean isVisible() {
// If the activity isn't hidden then it is considered visible and there is no need to check
@@ -4605,7 +4720,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// If in a transition, defer commits for activities that are going invisible
- if (!visible && mAtmService.getTransitionController().inTransition()) {
+ if (!visible && mAtmService.getTransitionController().inTransition(this)) {
return;
}
// If we are preparing an app transition, then delay changing
@@ -4724,9 +4839,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* this has become invisible.
*/
private void postApplyAnimation(boolean visible) {
+ final boolean usingShellTransitions =
+ mAtmService.getTransitionController().getTransitionPlayer() != null;
final boolean delayed = isAnimating(PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION);
- if (!delayed) {
+ if (!delayed && !usingShellTransitions) {
// We aren't delayed anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be delayed,
// which we might have done because we were in closing/opening apps list.
@@ -4745,7 +4862,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
- if (visible || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
+ if (visible || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)
+ || usingShellTransitions) {
setClientVisible(visible);
}
@@ -4854,7 +4972,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mCurrentLaunchCanTurnScreenOn;
}
- void setState(ActivityState state, String reason) {
+ void setState(State state, String reason) {
ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s",
this, getState(), state, reason);
@@ -4866,8 +4984,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mState = state;
- if (task != null) {
- task.onActivityStateChanged(this, state, reason);
+ if (getTaskFragment() != null) {
+ getTaskFragment().onActivityStateChanged(this, state, reason);
}
// The WindowManager interprets the app stopping signal as
@@ -4927,44 +5045,42 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- ActivityState getState() {
+ State getState() {
return mState;
}
/**
* Returns {@code true} if the Activity is in the specified state.
*/
- boolean isState(ActivityState state) {
+ boolean isState(State state) {
return state == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2) {
+ boolean isState(State state1, State state2) {
return state1 == mState || state2 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) {
+ boolean isState(State state1, State state2, State state3) {
return state1 == mState || state2 == mState || state3 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4) {
+ boolean isState(State state1, State state2, State state3, State state4) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4, ActivityState state5) {
+ boolean isState(State state1, State state2, State state3, State state4, State state5) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState
|| state5 == mState;
}
@@ -4972,8 +5088,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4, ActivityState state5, ActivityState state6) {
+ boolean isState(State state1, State state2, State state3, State state4, State state5,
+ State state6) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState
|| state5 == mState || state6 == mState;
}
@@ -5165,7 +5281,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// returns. Just need to confirm this reasoning makes sense.
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STARTED, STOPPING, STOPPED, PAUSED);
- if (deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
+ if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
// Go ahead and just put the activity in pip if it supports auto-pip.
mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
return;
@@ -5181,13 +5298,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
supportsEnterPipOnTaskSwitch = false;
break;
case RESUMED:
- // If the app is capable of entering PIP, we should try pausing it now
- // so it can PIP correctly.
- if (deferHidingClient) {
- task.startPausingLocked(false /* uiSleeping */,
- null /* resuming */, "makeInvisible");
- break;
- }
case INITIALIZING:
case PAUSING:
case PAUSED:
@@ -5284,7 +5394,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private boolean shouldBeResumed(ActivityRecord activeActivity) {
return shouldMakeActive(activeActivity) && isFocusable()
- && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE
+ && getTaskFragment().getVisibility(activeActivity)
+ == TASK_FRAGMENT_VISIBILITY_VISIBLE
&& canResumeByCompat();
}
@@ -5338,7 +5449,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!task.hasChild(this)) {
throw new IllegalStateException("Activity not found in its task");
}
- return task.topRunningActivity() == this;
+ return getTaskFragment().topRunningActivity() == this;
}
void handleAlreadyVisible() {
@@ -5427,16 +5538,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STATES, "Activity paused: token=%s, timeout=%b", appToken,
timeout);
- if (task != null) {
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
removePauseTimeout();
- final ActivityRecord pausingActivity = task.getPausingActivity();
+ final ActivityRecord pausingActivity = taskFragment.getPausingActivity();
if (pausingActivity == this) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this,
(timeout ? "(due to timeout)" : " (pause complete)"));
mAtmService.deferWindowLayout();
try {
- task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
+ taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */);
} finally {
mAtmService.continueWindowLayout();
}
@@ -6099,9 +6211,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return this;
}
// Try to use the one which is closest to top.
- ActivityRecord r = rootTask.getResumedActivity();
+ ActivityRecord r = rootTask.getTopResumedActivity();
if (r == null) {
- r = rootTask.getPausingActivity();
+ r = rootTask.getTopPausingActivity();
}
if (r != null) {
return r;
@@ -6179,7 +6291,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// This would be redundant.
return false;
}
- if (isState(RESUMED) || getRootTask() == null || this == task.getPausingActivity()
+ if (isState(RESUMED) || getRootTask() == null
+ || this == getTaskFragment().getPausingActivity()
|| !mHaveState || !stopped) {
// We're not ready for this kind of thing.
return false;
@@ -6706,8 +6819,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Configuration displayConfig = mDisplayContent.getConfiguration();
return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
- appRect, insets, thumbnailHeader, task, displayConfig.uiMode,
- displayConfig.orientation);
+ appRect, insets, thumbnailHeader, task, displayConfig.orientation);
}
@Override
@@ -7132,7 +7244,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If the activity has requested override bounds, the configuration needs to be
// computed accordingly.
if (!matchParentBounds()) {
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig,
+ newParentConfiguration);
}
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
@@ -7247,7 +7360,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Since bounds has changed, the configuration needs to be computed accordingly.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
/**
@@ -7263,8 +7376,53 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
- * change and the requested orientation is different from the parent.
+ * In some cases, applying insets to bounds changes the orientation. For example, if a
+ * close-to-square display rotates to portrait to respect a portrait orientation activity, after
+ * insets such as the status and nav bars are applied, the activity may actually have a
+ * landscape orientation. This method checks whether the orientations of the activity window
+ * with and without insets match or if the orientation with insets already matches the
+ * requested orientation. If not, it may be necessary to letterbox the window.
+ * @param parentBounds are the new parent bounds passed down to the activity and should be used
+ * to compute the stable bounds.
+ * @param outBounds will store the stable bounds, which are the bounds with insets applied.
+ * These should be used to compute letterboxed bounds if orientation is not
+ * respected when insets are applied.
+ */
+ private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) {
+ if (mDisplayContent == null) {
+ return true;
+ }
+ // Only need to make changes if activity sets an orientation
+ final int requestedOrientation = getRequestedConfigurationOrientation();
+ if (requestedOrientation == ORIENTATION_UNDEFINED) {
+ return true;
+ }
+ // Compute parent orientation from bounds
+ final int orientation = parentBounds.height() >= parentBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ // Compute orientation from stable parent bounds (= parent bounds with insets applied)
+ final Task task = getTask();
+ task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */,
+ outBounds /* outStableBounds */, parentBounds /* bounds */,
+ mDisplayContent.getDisplayInfo());
+ final int orientationWithInsets = outBounds.height() >= outBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ // If orientation does not match the orientation with insets applied, then a
+ // display rotation will not be enough to respect orientation. However, even if they do
+ // not match but the orientation with insets applied matches the requested orientation, then
+ // there is no need to modify the bounds because when insets are applied, the activity will
+ // have the desired orientation.
+ return orientation == orientationWithInsets
+ || orientationWithInsets == requestedOrientation;
+ }
+
+ /**
+ * Computes bounds (letterbox or pillarbox) when either:
+ * 1. The parent doesn't handle the orientation change and the requested orientation is
+ * different from the parent (see {@link DisplayContent#setIgnoreOrientationRequest()}.
+ * 2. The parent handling the orientation is not enough. This occurs when the display rotation
+ * may not be enough to respect orientation requests (see {@link
+ * ActivityRecord#orientationRespectedWithInsets}).
*
* <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
* in this method.
@@ -7272,9 +7430,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig,
int windowingMode) {
mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
- if (handlesOrientationChangeFromDescendant()) {
+ final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+ final Rect containerBounds = new Rect(parentBounds);
+ boolean orientationRespectedWithInsets =
+ orientationRespectedWithInsets(parentBounds, containerBounds);
+ if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) {
// No need to letterbox because of fixed orientation. Display will handle
- // fixed-orientation requests.
+ // fixed-orientation requests and a display rotation is enough to respect requested
+ // orientation with insets applied.
return;
}
if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable()) {
@@ -7294,7 +7457,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If the activity requires a different orientation (either by override or activityInfo),
// make it fit the available bounds by scaling down its bounds.
final int forcedOrientation = getRequestedConfigurationOrientation();
- if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
+ if (forcedOrientation == ORIENTATION_UNDEFINED
+ || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) {
return;
}
@@ -7306,67 +7470,83 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
- final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
- final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
- final Rect containingBounds = new Rect();
- final Rect containingAppBounds = new Rect();
- // Need to shrink the containing bounds into a square because the parent orientation does
- // not match the activity requested orientation.
- if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- // Shrink height to match width. Position height within app bounds.
- final int bottom = Math.min(parentAppBounds.top + parentBounds.width(),
- parentAppBounds.bottom);
- containingBounds.set(parentBounds.left, parentAppBounds.top, parentBounds.right,
- bottom);
- containingAppBounds.set(parentAppBounds.left, parentAppBounds.top,
- parentAppBounds.right, bottom);
- } else {
- // Shrink width to match height. Position width within app bounds.
- final int right = Math.min(parentAppBounds.left + parentBounds.height(),
- parentAppBounds.right);
- containingBounds.set(parentAppBounds.left, parentBounds.top, right,
- parentBounds.bottom);
- containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, right,
- parentAppBounds.bottom);
- }
-
- Rect mTmpFullBounds = new Rect(resolvedBounds);
- resolvedBounds.set(containingBounds);
+ // TODO(b/182268157) merge aspect ratio logic here and in
+ // {@link ActivityRecord#applyAspectRatio}
+ // if no aspect ratio constraints are provided, parent aspect ratio is used
+ float aspectRatio = computeAspectRatio(parentBounds);
// Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
// set-fixed-orientation-letterbox-aspect-ratio.
final float letterboxAspectRatioOverride =
mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
- final float desiredAspectRatio =
- letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
- ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
- // Apply aspect ratio to resolved bounds
- mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds,
- containingBounds, desiredAspectRatio, true);
-
- // Vertically center if orientation is landscape. Bounds will later be horizontally centered
- // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
+ aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? letterboxAspectRatioOverride : aspectRatio;
+
+ // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
+ // order to use the extra available space.
+ final float maxAspectRatio = info.getMaxAspectRatio();
+ final float minAspectRatio = info.getMinAspectRatio();
+ if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) {
+ aspectRatio = maxAspectRatio;
+ } else if (aspectRatio < minAspectRatio) {
+ aspectRatio = minAspectRatio;
+ }
+
+ // Store the current bounds to be able to revert to size compat mode values below if needed.
+ final Rect prevResolvedBounds = new Rect(resolvedBounds);
+
+ // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored
+ // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()},
+ // to ensure that aspect ratio is respected after insets are applied.
+ int activityWidth;
+ int activityHeight;
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- final int offsetY = parentBounds.centerY() - resolvedBounds.centerY();
- resolvedBounds.offset(0, offsetY);
+ activityWidth = parentBounds.width();
+ // Compute height from stable bounds width to ensure orientation respected after insets.
+ activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio);
+ // Landscape is defined as width > height. To ensure activity is landscape when aspect
+ // ratio is close to 1, reduce the height by one pixel.
+ if (activityWidth == activityHeight) {
+ activityHeight -= 1;
+ }
+ // Center vertically within stable bounds in landscape to ensure insets do not trim
+ // height.
+ final int top = containerBounds.centerY() - activityHeight / 2;
+ resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight);
+ } else {
+ activityHeight = parentBounds.height();
+ // Compute width from stable bounds height to ensure orientation respected after insets.
+ activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio);
+ // Center horizontally in portrait. For now, align to left and allow
+ // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center
+ // horizontally. Exclude left insets from parent to ensure cutout does not trim width.
+ final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ resolvedBounds.set(parentAppBounds.left, parentBounds.top,
+ parentAppBounds.left + activityWidth, parentBounds.bottom);
}
if (mCompatDisplayInsets != null) {
mCompatDisplayInsets.getBoundsByRotation(
mTmpBounds, newParentConfig.windowConfiguration.getRotation());
- if (resolvedBounds.width() != mTmpBounds.width()
- || resolvedBounds.height() != mTmpBounds.height()) {
+ // Insets may differ between different rotations, for example in the case of a display
+ // cutout. To ensure consistent bounds across rotations, compare the activity dimensions
+ // minus insets from the rotation the compat bounds were computed in.
+ Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds,
+ mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]);
+ if (activityWidth != mTmpBounds.width()
+ || activityHeight != mTmpBounds.height()) {
// The app shouldn't be resized, we only do fixed orientation letterboxing if the
// compat bounds are also from the same fixed orientation letterbox. Otherwise,
// clear the fixed orientation bounds to show app in size compat mode.
- resolvedBounds.set(mTmpFullBounds);
+ resolvedBounds.set(prevResolvedBounds);
return;
}
}
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
- task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
+ newParentConfig);
mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
}
@@ -7390,11 +7570,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// then they should be aligned later in #updateResolvedBoundsHorizontalPosition().
if (!mTmpBounds.isEmpty()) {
resolvedBounds.set(mTmpBounds);
+ // Exclude the horizontal decor area.
+ resolvedBounds.left = parentAppBounds.left;
}
if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
// Compute the configuration based on the resolved bounds. If aspect ratio doesn't
// restrict, the bounds should be the requested override bounds.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
getFixedRotationTransformDisplayInfo());
}
}
@@ -7450,14 +7632,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mIsAspectRatioApplied =
applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
}
+ // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
+ // the container app bounds. Otherwise the entire container bounds are available.
+ final boolean fillContainer = resolvedBounds.equals(containingBounds);
+ if (!fillContainer) {
+ // The horizontal position should not cover insets.
+ resolvedBounds.left = containingAppBounds.left;
+ }
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
// later, so the calculation is simpler that doesn't need to involve offset from parent.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
mCompatDisplayInsets);
// Use current screen layout as source because the size of app is independent to parent.
- resolvedConfig.screenLayout = Task.computeScreenLayoutOverride(
+ resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride(
getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
resolvedConfig.screenHeightDp);
@@ -7516,7 +7705,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
// if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
- final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
final int screenPosY = containerBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
@@ -7601,6 +7789,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (getUid() == SYSTEM_UID) {
return false;
}
+ // Do not sandbox to activity window bounds if the feature is disabled.
+ if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) {
+ return false;
+ }
// Never apply sandboxing to an app that should be explicitly excluded from the config.
if (info != null && info.neverSandboxDisplayApis()) {
return false;
@@ -7754,12 +7946,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return true;
}
- private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
- Rect containingBounds) {
- return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
- 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */);
- }
-
/**
* Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
* made to outBounds.
@@ -7768,19 +7954,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
- Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
+ Rect containingBounds) {
final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
final float minAspectRatio = info.getMinAspectRatio();
if (task == null || rootTask == null
- || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
- && !fixedOrientationLetterboxed)
- || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
+ || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
+ || (maxAspectRatio == 0 && minAspectRatio == 0)
|| isInVrUiMode(getConfiguration())) {
- // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
- // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we
- // are in VR mode.
+ // We don't enforce aspect ratio if the activity task is in multiwindow unless it
+ // is in size-compat mode. We also don't set it if we are in VR mode.
return false;
}
@@ -7788,30 +7972,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int containingAppHeight = containingAppBounds.height();
final float containingRatio = computeAspectRatio(containingAppBounds);
- if (desiredAspectRatio < 1) {
- desiredAspectRatio = containingRatio;
- }
-
- if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
- desiredAspectRatio = maxAspectRatio;
- } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
- desiredAspectRatio = minAspectRatio;
- }
-
int activityWidth = containingAppWidth;
int activityHeight = containingAppHeight;
- if (containingRatio > desiredAspectRatio) {
+ if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
if (containingAppWidth < containingAppHeight) {
// Width is the shorter side, so we use that to figure-out what the max. height
// should be given the aspect ratio.
- activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f);
+ activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
} else {
// Height is the shorter side, so we use that to figure-out what the max. width
// should be given the aspect ratio.
- activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
+ activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
}
- } else if (containingRatio < desiredAspectRatio) {
+ } else if (containingRatio < minAspectRatio) {
boolean adjustWidth;
switch (getRequestedConfigurationOrientation()) {
case ORIENTATION_LANDSCAPE:
@@ -7839,9 +8013,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
break;
}
if (adjustWidth) {
- activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f);
+ activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
} else {
- activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f);
+ activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
}
}
@@ -7865,13 +8039,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
- // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
- // container app bounds. Otherwise the entire container bounds are available.
- if (!outBounds.equals(containingBounds)) {
- // The horizontal position should not cover insets.
- outBounds.left = containingAppBounds.left;
- }
-
return true;
}
@@ -8024,7 +8191,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
- startFreezingScreenLocked(globalChanges);
+ if (!mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ startFreezingScreenLocked(globalChanges);
+ }
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
@@ -8627,6 +8796,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* compatibility mode activity compute the configuration without relying on its current display.
*/
static class CompatDisplayInsets {
+ /** The original rotation the compat insets were computed in */
+ final @Rotation int mOriginalRotation;
/** The container width on rotation 0. */
private final int mWidth;
/** The container height on rotation 0. */
@@ -8653,6 +8824,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Constructs the environment to simulate the bounds behavior of the given container. */
CompatDisplayInsets(DisplayContent display, ActivityRecord container,
@Nullable Rect fixedOrientationBounds) {
+ mOriginalRotation = display.getRotation();
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
if (mIsFloating) {
final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -8799,7 +8971,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
outAppBounds.offset(insets.left, insets.top);
} else if (rotation != ROTATION_UNDEFINED) {
// Ensure the app bounds won't overlap with insets.
- Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
+ TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
+ mNonDecorInsets[rotation]);
}
}
}
@@ -8868,6 +9041,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
+ @Override
+ void finishSync(Transaction outMergedTransaction, boolean cancel) {
+ // This override is just for getting metrics. allFinished needs to be checked before
+ // finish because finish resets all the states.
+ mLastAllReadyAtSync = allSyncFinished();
+ super.finishSync(outMergedTransaction, cancel);
+ }
+
static class Builder {
private final ActivityTaskManagerService mAtmService;
private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 8540fa7cf347..30c7b232fcc8 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import android.util.ArraySet;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b6f2f243040e..d08d285beda8 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -27,6 +27,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -38,6 +39,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -488,6 +490,16 @@ public class ActivityStartController {
return START_SUCCESS;
}
+ void startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
+ @NonNull Intent activityIntent, @Nullable Bundle activityOptions) {
+ obtainStarter(activityIntent, "startActivityInTaskFragment")
+ .setActivityOptions(activityOptions)
+ .setInTaskFragment(taskFragment)
+ .setCallingUid(Binder.getCallingUid())
+ .setCallingPid(Binder.getCallingPid())
+ .execute();
+ }
+
void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a9a25fc2d272..5c9fdc3580e8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -28,6 +28,7 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -58,6 +59,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -74,7 +76,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -177,6 +178,7 @@ class ActivityStarter {
private int mPreferredWindowingMode;
private Task mInTask;
+ private TaskFragment mInTaskFragment;
@VisibleForTesting
boolean mAddingToTask;
private Task mReuseTask;
@@ -342,6 +344,7 @@ class ActivityStarter {
boolean avoidMoveToFront;
ActivityRecord[] outActivity;
Task inTask;
+ TaskFragment inTaskFragment;
String reason;
ProfilerInfo profilerInfo;
Configuration globalConfig;
@@ -392,6 +395,7 @@ class ActivityStarter {
componentSpecified = false;
outActivity = null;
inTask = null;
+ inTaskFragment = null;
reason = null;
profilerInfo = null;
globalConfig = null;
@@ -407,7 +411,7 @@ class ActivityStarter {
/**
* Adopts all values from passed in request.
*/
- void set(Request request) {
+ void set(@NonNull Request request) {
caller = request.caller;
intent = request.intent;
intentGrants = request.intentGrants;
@@ -432,6 +436,7 @@ class ActivityStarter {
componentSpecified = request.componentSpecified;
outActivity = request.outActivity;
inTask = request.inTask;
+ inTaskFragment = request.inTaskFragment;
reason = request.reason;
profilerInfo = request.profilerInfo;
globalConfig = request.globalConfig;
@@ -574,6 +579,7 @@ class ActivityStarter {
mPreferredWindowingMode = starter.mPreferredWindowingMode;
mInTask = starter.mInTask;
+ mInTaskFragment = starter.mInTaskFragment;
mAddingToTask = starter.mAddingToTask;
mReuseTask = starter.mReuseTask;
@@ -835,6 +841,7 @@ class ActivityStarter {
final int startFlags = request.startFlags;
final SafeActivityOptions options = request.activityOptions;
Task inTask = request.inTask;
+ TaskFragment inTaskFragment = request.inTaskFragment;
int err = ActivityManager.START_SUCCESS;
// Pull the optional Ephemeral Installer-only bundle out of the options early.
@@ -1175,8 +1182,8 @@ class ActivityStarter {
}
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
- request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
- restrictedBgActivity, intentGrants);
+ request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
+ inTask, inTaskFragment, restrictedBgActivity, intentGrants);
if (request.outActivity != null) {
request.outActivity[0] = mLastStartActivityRecord;
@@ -1546,9 +1553,10 @@ class ActivityStarter {
* Here also ensures that the starting activity is removed if the start wasn't successful.
*/
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, Task inTask,
- boolean restrictedBgActivity, NeededUriGrants intentGrants) {
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ int startFlags, boolean doResume, ActivityOptions options, Task inTask,
+ TaskFragment inTaskFragment, boolean restrictedBgActivity,
+ NeededUriGrants intentGrants) {
int result = START_CANCELED;
final Task startedActivityRootTask;
@@ -1564,11 +1572,17 @@ class ActivityStarter {
newTransition.setRemoteTransition(remoteTransition);
}
mService.getTransitionController().collect(r);
+ // TODO(b/188669821): Remove when navbar reparenting moves to shell
+ if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null
+ && r.getOptions().getTransientLaunch()) {
+ mService.getTransitionController().setIsLegacyRecents();
+ }
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
+ startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
+ intentGrants);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
startedActivityRootTask = handleStartResult(r, result);
@@ -1595,7 +1609,8 @@ class ActivityStarter {
statusBar.collapsePanels();
}
}
- if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
+ final boolean started = result == START_SUCCESS || result == START_TASK_TO_FRONT;
+ if (started) {
// The activity is started new rather than just brought forward, so record
// it as an existence change.
mService.getTransitionController().collectExistenceChange(r);
@@ -1603,9 +1618,9 @@ class ActivityStarter {
if (newTransition != null) {
mService.getTransitionController().requestStartTransition(newTransition,
mTargetTask, remoteTransition);
- } else {
+ } else if (started) {
// Make the collecting transition wait until this request is ready.
- mService.getTransitionController().setReady(false);
+ mService.getTransitionController().setReady(r, false);
}
}
}
@@ -1665,15 +1680,15 @@ class ActivityStarter {
*
* Note: This method should only be called from {@link #startActivityUnchecked}.
*/
-
// TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
@VisibleForTesting
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
- boolean restrictedBgActivity, NeededUriGrants intentGrants) {
- setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
- voiceInteractor, restrictedBgActivity);
+ TaskFragment inTaskFragment, boolean restrictedBgActivity,
+ NeededUriGrants intentGrants) {
+ setInitialState(r, options, inTask, inTaskFragment, doResume, startFlags, sourceRecord,
+ voiceSession, voiceInteractor, restrictedBgActivity);
computeLaunchingTaskFlags();
@@ -1772,7 +1787,7 @@ class ActivityStarter {
mStartActivity.logStartActivity(
EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask());
- mTargetRootTask.mLastPausedActivity = null;
+ mStartActivity.getTaskFragment().clearLastPausedActivity();
mRootWindowContainer.startPowerModeLaunchIfNeeded(
false /* forceSend */, mStartActivity);
@@ -2049,7 +2064,7 @@ class ActivityStarter {
}
// For paranoia, make sure we have correctly resumed the top activity.
- topRootTask.mLastPausedActivity = null;
+ top.getTaskFragment().clearLastPausedActivity();
if (mDoResume) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -2145,7 +2160,7 @@ class ActivityStarter {
task.moveActivityToFrontLocked(act);
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
- mTargetRootTask.mLastPausedActivity = null;
+ act.getTaskFragment().clearLastPausedActivity();
} else {
mAddingToTask = true;
}
@@ -2213,6 +2228,7 @@ class ActivityStarter {
mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
mInTask = null;
+ mInTaskFragment = null;
mAddingToTask = false;
mReuseTask = null;
@@ -2240,9 +2256,9 @@ class ActivityStarter {
}
private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask,
- boolean doResume, int startFlags, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- boolean restrictedBgActivity) {
+ TaskFragment inTaskFragment, boolean doResume, int startFlags,
+ ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession,
+ IVoiceInteractor voiceInteractor, boolean restrictedBgActivity) {
reset(false /* clearRequest */);
mStartActivity = r;
@@ -2345,6 +2361,7 @@ class ActivityStarter {
Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
mInTask = null;
}
+ mInTaskFragment = inTaskFragment;
mStartFlags = startFlags;
// If the onlyIfNeeded flag is set, then we can do this if the activity being launched
@@ -2573,7 +2590,7 @@ class ActivityStarter {
*/
private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity) {
mTargetRootTask = intentActivity.getRootTask();
- mTargetRootTask.mLastPausedActivity = null;
+ intentActivity.getTaskFragment().clearLastPausedActivity();
Task intentTask = intentActivity.getTask();
// If the target task is not in the front, then we need to bring it to the front...
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
@@ -2700,11 +2717,23 @@ class ActivityStarter {
mIntentDelivered = true;
}
- private void addOrReparentStartingActivity(Task parent, String reason) {
- if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
- parent.addChild(mStartActivity);
+ private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
+ TaskFragment newParent = task;
+ if (mInTaskFragment != null) {
+ // mInTaskFragment is created and added to the leaf task by task fragment organizer's
+ // request. If the task was resolved and different than mInTaskFragment, reparent the
+ // task to mInTaskFragment for embedding.
+ if (mInTaskFragment.getTask() != task) {
+ task.reparent(mInTaskFragment, POSITION_TOP);
+ } else {
+ newParent = mInTaskFragment;
+ }
+ }
+ if (mStartActivity.getTaskFragment() == null
+ || mStartActivity.getTaskFragment() == newParent) {
+ newParent.addChild(mStartActivity, POSITION_TOP);
} else {
- mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
+ mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
}
}
@@ -2936,6 +2965,11 @@ class ActivityStarter {
return this;
}
+ ActivityStarter setInTaskFragment(TaskFragment taskFragment) {
+ mRequest.inTaskFragment = taskFragment;
+ return this;
+ }
+
ActivityStarter setWaitResult(WaitResult result) {
mRequest.waitResult = result;
return this;
@@ -3019,5 +3053,7 @@ class ActivityStarter {
pw.print(mDoResume);
pw.print(" mAddingToTask=");
pw.println(mAddingToTask);
+ pw.print(" mInTaskFragment=");
+ pw.println(mInTaskFragment);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 04c01736431d..eba3e8551753 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -253,7 +253,6 @@ import com.android.server.am.PendingIntentController;
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.UserState;
import com.android.server.firewall.IntentFirewall;
-import com.android.server.inputmethod.InputMethodSystemProperty;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -1222,8 +1221,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
final Task topFocusedRootTask = getTopDisplayFocusedRootTask();
- if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null
- && topFocusedRootTask.getResumedActivity().info.applicationInfo.uid
+ if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
+ && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
mAppSwitchesAllowed = true;
}
@@ -1881,25 +1880,42 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void setFocusedTask(int taskId) {
enforceTaskPermission("setFocusedTask()");
- ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId);
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_ATTACHED_TASK_ONLY);
- if (task == null) {
- return;
- }
- final ActivityRecord r = task.topRunningActivityLocked();
- if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
+ setFocusedTask(taskId, null /* touchedActivity */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
+ void setFocusedTask(int taskId, ActivityRecord touchedActivity) {
+ ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d touchedActivity=%s", taskId,
+ touchedActivity);
+ final Task task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY);
+ if (task == null) {
+ return;
+ }
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (r == null) {
+ return;
+ }
+
+ if (r.moveFocusableActivityToTop("setFocusedTask")) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ } else if (touchedActivity != null && touchedActivity != r
+ && touchedActivity.getTask() == r.getTask()
+ && touchedActivity.getTaskFragment() != r.getTaskFragment()) {
+ // Set the focused app directly since the focused window is not on the
+ // top-most TaskFragment of the top-most Task
+ final DisplayContent displayContent = touchedActivity.getDisplayContent();
+ displayContent.setFocusedApp(touchedActivity);
+ mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /* updateInputWindows */);
+ }
+ }
+
@Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
@@ -3769,6 +3785,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ @Override
+ public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "detachNavigationBarFromApp");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ getTransitionController().legacyDetachNavigationBarFromApp(transition);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
@@ -5036,11 +5066,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* @param imeContainer The DisplayArea that contains the IME window.
*/
void onImeWindowSetOnDisplayArea(final int pid, @NonNull final DisplayArea imeContainer) {
- // Don't update process-level configuration for Multi-Client IME process since other
- // IMEs on other displays will also receive this configuration change due to IME
- // services use the same application config/context.
- if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
-
if (pid == MY_PID || pid < 0) {
ProtoLog.w(WM_DEBUG_CONFIGURATION,
"Trying to update display configuration for system/invalid process.");
@@ -5055,6 +5080,34 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
process.registerDisplayAreaConfigurationListener(imeContainer);
}
+ @Override
+ public void setRunningRemoteTransitionDelegate(IApplicationThread caller) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "setRunningRemoteTransition");
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ // Also only allow a process which is already runningRemoteAnimation to mark another
+ // process.
+ final WindowProcessController callingProc = getProcessController(callingPid,
+ callingUid);
+ if (callingProc == null || !callingProc.isRunningRemoteTransition()) {
+ final String msg = "Can't call setRunningRemoteTransition from a process (pid="
+ + callingPid + " uid=" + callingUid + ") which isn't itself running a "
+ + "remote transition.";
+ Slog.e(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final WindowProcessController wpc = getProcessController(caller);
+ if (wpc == null) {
+ Slog.w(TAG, "Unable to find process for application " + caller);
+ return;
+ }
+ wpc.setRunningRemoteAnimation(true /* running */);
+ callingProc.addRemoteAnimationDelegate(wpc);
+ }
+ }
+
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index d3d1c1ca6a2b..f6cca84fd5f4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -48,6 +48,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
@@ -72,8 +75,6 @@ import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_R
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.TAG_CLEANUP;
@@ -136,7 +137,6 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
-import com.android.internal.os.TransferPipe;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -146,7 +146,6 @@ import com.android.server.am.UserState;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -348,6 +347,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
*/
private int mVisibilityTransactionDepth;
+ /**
+ * Whether to the visibility updates that started from {@code RootWindowContainer} should be
+ * deferred.
+ */
+ private boolean mDeferRootVisibilityUpdate;
+
private ActivityMetricsLogger mActivityMetricsLogger;
/** Check if placing task or activity on specified display is allowed. */
@@ -1354,7 +1359,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT,
- 0 /* flags */, task, options != null ? options.getRemoteTransition() : null);
+ 0 /* flags */, task, task /* readyGroupRef */,
+ options != null ? options.getRemoteTransition() : null);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -1968,76 +1974,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
- String innerPrefix = null;
- String[] args = null;
boolean printed = false;
- for (int i=list.size()-1; i>=0; i--) {
+ for (int i = list.size() - 1; i >= 0; i--) {
final ActivityRecord r = list.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
- continue;
- }
- if (innerPrefix == null) {
- innerPrefix = prefix + " ";
- args = new String[0];
- }
- printed = true;
- final boolean full = !brief && (complete || !r.isInHistory());
- if (needNL) {
- pw.println("");
- needNL = false;
- }
- if (header != null) {
- header.run();
- header = null;
- }
- if (lastTask != r.getTask()) {
- lastTask = r.getTask();
- pw.print(prefix);
- pw.print(full ? "* " : " ");
- pw.println(lastTask);
- if (full) {
- lastTask.dump(pw, prefix + " ");
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- if (lastTask.intent != null) {
- pw.print(prefix); pw.print(" ");
- pw.println(lastTask.intent.toInsecureString());
- }
- }
- }
- pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(r);
- if (full) {
- r.dump(pw, innerPrefix, true /* dumpAll */);
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
- if (r.app != null) {
- pw.print(innerPrefix); pw.println(r.app);
- }
- }
- if (client && r.attachedToProcess()) {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.getThread().dumpActivity(
- tp.getWriteFd(), r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(innerPrefix + "Failure while dumping the activity: " + e);
- } catch (RemoteException e) {
- pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
- }
- needNL = true;
- }
+ ActivityRecord.dumpActivity(fd, pw, i, r, prefix, label, complete, brief,
+ client, dumpPackage, needNL, header, lastTask);
+ lastTask = r.getTask();
+ header = null;
+ needNL = client && r.attachedToProcess();
}
return printed;
}
@@ -2064,7 +2008,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
void updateTopResumedActivityIfNeeded() {
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topRootTask == null || topRootTask.getResumedActivity() == prevTopActivity) {
+ if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) {
if (mService.isSleepingLocked()) {
// There won't be a next resumed activity. The top process should still be updated
// according to the current top focused activity.
@@ -2086,7 +2030,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
// Update the current top activity.
- mTopResumedActivity = topRootTask.getResumedActivity();
+ mTopResumedActivity = topRootTask.getTopResumedActivity();
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
@@ -2326,6 +2270,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return mVisibilityTransactionDepth > 0;
}
+ void setDeferRootVisibilityUpdate(boolean deferUpdate) {
+ mDeferRootVisibilityUpdate = deferUpdate;
+ }
+
+ boolean isRootVisibilityUpdateDeferred() {
+ return mDeferRootVisibilityUpdate;
+ }
+
/**
* Called when the state or visibility of an attached activity is changed.
*
@@ -2393,8 +2345,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
String processName = null;
int uid = 0;
synchronized (mService.mGlobalLock) {
- if (r.attachedToProcess()
- && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
processName = r.app.mName;
uid = r.app.mUid;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1b287ff8077..ac687dc064ce 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -78,10 +78,7 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
@@ -102,12 +99,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Picture;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Debug;
@@ -132,7 +124,6 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
-import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.ScaleAnimation;
@@ -641,24 +632,6 @@ public class AppTransition implements Dump {
/**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth,
- int appHeight, long duration, Interpolator interpolator) {
- if (a != null) {
- if (duration > 0) {
- a.setDuration(duration);
- }
- a.setFillAfter(true);
- if (interpolator != null) {
- a.setInterpolator(interpolator);
- }
- a.initialize(appWidth, appHeight, appWidth, appHeight);
- }
- return a;
- }
-
- /**
- * Prepares the specified animation with a standard duration, interpolator, etc.
- */
Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
@@ -678,56 +651,16 @@ public class AppTransition implements Dump {
}
/**
- * Return the current thumbnail transition state.
- */
- int getThumbnailTransitionState(boolean enter) {
- if (enter) {
- if (mNextAppTransitionScaleUp) {
- return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
- } else {
- return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
- }
- } else {
- if (mNextAppTransitionScaleUp) {
- return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
- } else {
- return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
- }
- }
- }
-
- /**
* Creates an overlay with a background color and a thumbnail for the cross profile apps
* animation.
*/
HardwareBuffer createCrossProfileAppsThumbnail(
@DrawableRes int thumbnailDrawableRes, Rect frame) {
- final int width = frame.width();
- final int height = frame.height();
-
- final Picture picture = new Picture();
- final Canvas canvas = picture.beginRecording(width, height);
- canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
- final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
- final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
- drawable.setBounds(
- (width - thumbnailSize) / 2,
- (height - thumbnailSize) / 2,
- (width + thumbnailSize) / 2,
- (height + thumbnailSize) / 2);
- drawable.setTint(mContext.getColor(android.R.color.white));
- drawable.draw(canvas);
- picture.endRecording();
-
- return Bitmap.createBitmap(picture).getHardwareBuffer();
+ return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
}
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
- final Animation animation =
- mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation();
- return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
- appRect.height(), 0, null);
+ return mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(appRect);
}
/**
@@ -735,115 +668,14 @@ public class AppTransition implements Dump {
* when a thumbnail is specified with the pending animation override.
*/
Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
- HardwareBuffer thumbnailHeader, WindowContainer container, int uiMode,
- int orientation) {
- Animation a;
- final int thumbWidthI = thumbnailHeader.getWidth();
- final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = thumbnailHeader.getHeight();
- final int appWidth = appRect.width();
-
- float scaleW = appWidth / thumbWidth;
- getNextAppTransitionStartRect(container, mTmpRect);
- final float fromX;
- float fromY;
- final float toX;
- float toY;
- final float pivotX;
- final float pivotY;
- if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
- fromX = mTmpRect.left;
- fromY = mTmpRect.top;
-
- // For the curved translate animation to work, the pivot points needs to be at the
- // same absolute position as the one from the real surface.
- toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
- toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
- pivotX = mTmpRect.width() / 2;
- pivotY = appRect.height() / 2 / scaleW;
- if (mGridLayoutRecentsEnabled) {
- // In the grid layout, the header is displayed above the thumbnail instead of
- // overlapping it.
- fromY -= thumbHeightI;
- toY -= thumbHeightI * scaleW;
- }
- } else {
- pivotX = 0;
- pivotY = 0;
- fromX = mTmpRect.left;
- fromY = mTmpRect.top;
- toX = appRect.left;
- toY = appRect.top;
- }
- final long duration = getAspectScaleDuration();
- final Interpolator interpolator = getAspectScaleInterpolator();
- if (mNextAppTransitionScaleUp) {
- // Animation up from the thumbnail to the full screen
- Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
- scale.setInterpolator(interpolator);
- scale.setDuration(duration);
- Animation alpha = new AlphaAnimation(1f, 0f);
- alpha.setInterpolator(mThumbnailFadeOutInterpolator);
- alpha.setDuration(duration);
- Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
- translate.setInterpolator(interpolator);
- translate.setDuration(duration);
-
- mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
- mTmpToClipRect.set(appRect);
-
- // Containing frame is in screen space, but we need the clip rect in the
- // app space.
- mTmpToClipRect.offsetTo(0, 0);
- mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
- mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
-
- if (contentInsets != null) {
- mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
- (int) (-contentInsets.top * scaleW),
- (int) (-contentInsets.right * scaleW),
- (int) (-contentInsets.bottom * scaleW));
- }
-
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- clipAnim.setInterpolator(interpolator);
- clipAnim.setDuration(duration);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
- set.addAnimation(translate);
- set.addAnimation(clipAnim);
- a = set;
- } else {
- // Animation down from the full screen to the thumbnail
- Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
- scale.setInterpolator(interpolator);
- scale.setDuration(duration);
- Animation alpha = new AlphaAnimation(0f, 1f);
- alpha.setInterpolator(mThumbnailFadeInInterpolator);
- alpha.setDuration(duration);
- Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
- translate.setInterpolator(interpolator);
- translate.setDuration(duration);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
- set.addAnimation(translate);
- a = set;
-
- }
- return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
- null);
+ HardwareBuffer thumbnailHeader, WindowContainer container, int orientation) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+ container.hashCode());
+ return mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(appRect,
+ contentInsets, thumbnailHeader, orientation, spec != null ? spec.rect : null,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null,
+ mNextAppTransitionScaleUp);
}
private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
@@ -1043,7 +875,7 @@ public class AppTransition implements Dump {
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = mTransitionAnimation.createClipRevealAnimationLocked(
+ a = mTransitionAnimation.createClipRevealAnimationLockedCompat(
transit, enter, frame, displayFrame,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
@@ -1052,7 +884,7 @@ public class AppTransition implements Dump {
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
- a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame,
+ a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1064,8 +896,8 @@ public class AppTransition implements Dump {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
- a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), frame, transit, thumbnailHeader,
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter,
+ mNextAppTransitionScaleUp, frame, transit, thumbnailHeader,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1080,9 +912,9 @@ public class AppTransition implements Dump {
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
container.hashCode());
- a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), orientation, transit, frame,
- insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null,
+ a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter,
+ mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets,
+ stableInsets, freeform, spec != null ? spec.rect : null,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index faeb4ba36748..4355b3855bb7 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -166,6 +166,10 @@ class BLASTSyncEngine {
setReady(id, true);
}
+ boolean isReady(int id) {
+ return mActiveSyncs.get(id).mReady;
+ }
+
/**
* Aborts the sync (ie. it doesn't wait for ready or anything to finish)
*/
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index d52e9b608cdb..6fafc0291427 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -34,6 +34,7 @@ import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_
import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -111,6 +112,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* This method should be used for getting settings applied in each particular level of the
* hierarchy.
*/
+ @NonNull
public Configuration getConfiguration() {
return mFullConfiguration;
}
@@ -170,11 +172,13 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
/** Returns requested override configuration applied to this configuration container. */
+ @NonNull
public Configuration getRequestedOverrideConfiguration() {
return mRequestedOverrideConfiguration;
}
/** Returns the resolved override configuration. */
+ @NonNull
Configuration getResolvedOverrideConfiguration() {
return mResolvedOverrideConfiguration;
}
@@ -203,6 +207,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* Get merged override configuration from the top of the hierarchy down to this particular
* instance. This should be reported to client as override config.
*/
+ @NonNull
public Configuration getMergedOverrideConfiguration() {
return mMergedOverrideConfiguration;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index baa27e34d625..99f6fd4771b7 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -495,8 +495,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
DisplayAreaInfo getDisplayAreaInfo() {
- DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
+ final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
getDisplayContent().getDisplayId(), mFeatureId);
+ final RootDisplayArea root = getRootDisplayArea();
+ info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId;
info.configuration.setTo(getConfiguration());
return info;
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 47d7c9d1279d..3d7ac6c1a3f8 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -135,12 +136,6 @@ import java.util.function.BiFunction;
*/
class DisplayAreaPolicyBuilder {
- /**
- * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the
- * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)}
- */
- static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
-
@Nullable private HierarchyBuilder mRootHierarchyBuilder;
private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
new ArrayList<>();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a72d9aa9ec6b..ed6d6e5b8443 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -61,7 +61,6 @@ import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -94,6 +93,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_A
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
@@ -116,7 +116,6 @@ import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -166,13 +165,11 @@ import android.graphics.Region.Op;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerInternal;
import android.metrics.LogMaker;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -197,7 +194,6 @@ import android.view.ISystemGestureExclusionListener;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
@@ -361,6 +357,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
boolean mIsSizeForced = false;
/**
+ * Overridden display size and metrics to activity window bounds. Set via
+ * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging.
+ * @see WindowManagerService#setSandboxDisplayApis(int, boolean)
+ */
+ private boolean mSandboxDisplayApis = true;
+
+ /**
* Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity}
* but can be set from Settings or via shell command "adb shell wm density".
* @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
@@ -634,8 +637,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private WindowState mParentWindow;
private Point mLocationInParentWindow = new Point();
- private SurfaceControl mParentSurfaceControl;
- private InputWindowHandle mPortalWindowHandle;
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
@@ -689,6 +690,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// well and thus won't change the top resumed / focused record
boolean mDontMoveToTop;
+ private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -763,6 +766,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTmpWindow = null;
return true;
}
+
+ if (focusedApp.getTask() == activity.getTask()
+ && focusedApp.getTaskFragment() != activity.getTaskFragment()) {
+ // Do not use the activity window of another TaskFragment in the same leaf Task
+ return false;
+ }
}
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
@@ -1012,6 +1021,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
+ mAtmService.getTransitionController().registerLegacyListener(
+ mWmService.mActivityManagerAppTransitionNotifier);
mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mAppTransitionController = new AppTransitionController(mWmService, this);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
@@ -1909,10 +1920,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
}
-
- if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onRotationChanged(this);
- }
}
void configureDisplayPolicy() {
@@ -2480,7 +2487,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
boolean isVisibleRequested() {
- return isVisible();
+ return isVisible() && !mRemoved && !mRemoving;
}
@Override
@@ -2974,7 +2981,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
void removeIfPossible() {
- if (isAnimating(TRANSITION | PARENTS)) {
+ if (isAnimating(TRANSITION | PARENTS)
+ // isAnimating is a legacy transition query and will be removed, so also add a
+ // check for whether this is in a shell-transition when not using legacy.
+ || mAtmService.getTransitionController().inTransition()) {
mDeferredRemoval = true;
return;
}
@@ -3095,7 +3105,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
- mAppTransition.dumpDebug(proto, APP_TRANSITION);
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION);
+ } else {
+ mAppTransition.dumpDebug(proto, APP_TRANSITION);
+ }
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -4352,14 +4366,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private void updateBounds() {
calculateBounds(mDisplayInfo, mTmpBounds);
setBounds(mTmpBounds);
- if (mPortalWindowHandle != null && mParentSurfaceControl != null) {
- mPortalWindowHandle.touchableRegion.getBounds(mTmpRect);
- if (!mTmpBounds.equals(mTmpRect)) {
- mPortalWindowHandle.touchableRegion.set(mTmpBounds);
- getPendingTransaction().setInputWindowInfo(
- mParentSurfaceControl, mPortalWindowHandle);
- }
- }
}
// Determines the current display bounds based on the current state
@@ -4499,6 +4505,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ // clear first just in case.
+ mTmpActivityList.clear();
// Time to remove any exiting applications?
forAllRootTasks(task -> {
final ArrayList<ActivityRecord> activities = task.mExitingActivities;
@@ -4506,16 +4514,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final ActivityRecord activity = activities.get(j);
if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
&& (!activity.mIsExiting || activity.isEmpty())) {
- // Make sure there is no animation running on this activity, so any windows
- // associated with it will be removed as soon as their animations are
- // complete.
- cancelAnimation();
- ProtoLog.v(WM_DEBUG_ADD_REMOVE,
- "performLayout: Activity exiting now removed %s", activity);
- activity.removeIfPossible();
+ mTmpActivityList.add(activity);
}
}
});
+ if (!mTmpActivityList.isEmpty()) {
+ // Make sure there is no animation running on this activity, so any windows
+ // associated with it will be removed as soon as their animations are
+ // complete.
+ cancelAnimation();
+ }
+ for (int i = 0; i < mTmpActivityList.size(); ++i) {
+ final ActivityRecord activity = mTmpActivityList.get(i);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "performLayout: Activity exiting now removed %s", activity);
+ activity.removeIfPossible();
+ }
+ // Clear afterwards so we don't hold references.
+ mTmpActivityList.clear();
}
@Override
@@ -4962,7 +4978,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@WindowManager.TransitionFlags int flags) {
prepareAppTransition(transit, flags);
mAtmService.getTransitionController().requestTransitionIfNeeded(transit, flags,
- null /* trigger */);
+ null /* trigger */, this);
}
/** @see #requestTransitionAndLegacyPrepare(int, int) */
@@ -4970,11 +4986,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Nullable WindowContainer trigger) {
prepareAppTransition(transit);
mAtmService.getTransitionController().requestTransitionIfNeeded(transit, 0 /* flags */,
- trigger);
+ trigger, this);
}
void executeAppTransition() {
- mAtmService.getTransitionController().setReady();
+ mAtmService.getTransitionController().setReady(this);
if (mAppTransition.isTransitionSet()) {
ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
"Execute app transition: %s, displayId: %d Callers=%s",
@@ -5017,6 +5033,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
+ // TODO(b/191375840): decouple "forwardness" from transition system.
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ @WindowManager.TransitionType int type =
+ mAtmService.getTransitionController().getCollectingTransitionType();
+ return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
+ }
return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
|| mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
}
@@ -5340,31 +5362,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
- * Create a portal window handle for input. This window transports any touch to the display
- * indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
- *
- * @param name The name of the portal window handle.
- * @return the new portal window handle.
- */
- private InputWindowHandle createPortalWindowHandle(String name) {
- // Let surface flinger to set the display ID of this input window handle because we don't
- // know which display the parent surface control is on.
- final InputWindowHandle portalWindowHandle = new InputWindowHandle(
- null /* inputApplicationHandle */, INVALID_DISPLAY);
- portalWindowHandle.name = name;
- portalWindowHandle.token = new Binder();
- portalWindowHandle.layoutParamsFlags =
- FLAG_SPLIT_TOUCH | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL;
- getBounds(mTmpBounds);
- portalWindowHandle.touchableRegion.set(mTmpBounds);
- portalWindowHandle.scaleFactor = 1f;
- portalWindowHandle.ownerPid = Process.myPid();
- portalWindowHandle.ownerUid = Process.myUid();
- portalWindowHandle.portalToDisplayId = mDisplayId;
- return portalWindowHandle;
- }
-
- /**
* @see IWindowManager#setForwardedInsets
*/
public void setForwardedInsets(Insets insets) {
@@ -5600,6 +5597,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}
+ @Override
+ void onResize() {
+ super.onResize();
+ if (mWmService.mAccessibilityController != null) {
+ mWmService.mAccessibilityController.onDisplaySizeChanged(this);
+ }
+ }
+
/**
* If the launching rotated activity ({@link #mFixedRotationLaunchingApp}) is null, it simply
* applies the rotation to display. Otherwise because the activity has shown as rotated, the
@@ -5840,6 +5845,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return true;
}
+ /**
+ * Sets if Display APIs should be sandboxed to the activity window bounds.
+ */
+ void setSandboxDisplayApis(boolean sandboxDisplayApis) {
+ mSandboxDisplayApis = sandboxDisplayApis;
+ }
+
+ /**
+ * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds,
+ * {@code false} otherwise. Default to true, unless set for debugging purposes.
+ */
+ boolean sandboxDisplayApis() {
+ return mSandboxDisplayApis;
+ }
+
/** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 73d31bf7e0c8..212f50b762be 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,12 +16,9 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.util.RotationUtils.deltaRotation;
@@ -39,6 +36,7 @@ import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.ViewRootImpl.computeWindowBounds;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -173,6 +171,9 @@ import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -313,17 +314,41 @@ public class DisplayPolicy {
private WindowState mSystemUiControllingWindow;
+ // Candidate window to determine the color of navigation bar. The window needs to be top
+ // fullscreen-app windows or dim layers that are intersecting with the window frame of status
+ // bar.
+ private WindowState mNavBarColorWindowCandidate;
+
+ // The window to determine opacity and background of translucent navigation bar. The window
+ // needs to be opaque.
+ private WindowState mNavBarBackgroundWindow;
+
+ /**
+ * Windows to determine the color of status bar. See {@link #mNavBarColorWindowCandidate} for
+ * the conditions of being candidate window.
+ */
+ private final ArrayList<WindowState> mStatusBarColorWindows = new ArrayList<>();
+
+ /**
+ * Windows to determine opacity and background of translucent status bar. The window needs to be
+ * opaque
+ */
+ private final ArrayList<WindowState> mStatusBarBackgroundWindows = new ArrayList<>();
+
+ private String mFocusedApp;
private int mLastDisableFlags;
private int mLastAppearance;
- private int mLastFullscreenAppearance;
- private int mLastDockedAppearance;
private int mLastBehavior;
- private final Rect mNonDockedRootTaskBounds = new Rect();
- private final Rect mDockedRootTaskBounds = new Rect();
- private final Rect mLastNonDockedRootTaskBounds = new Rect();
- private final Rect mLastDockedRootTaskBounds = new Rect();
+ private final InsetsState mRequestedState = new InsetsState();
+ private AppearanceRegion[] mLastStatusBarAppearanceRegions;
- // What we last reported to system UI about whether the focused window is fullscreen/immersive.
+ /** The union of checked bounds while fetching {@link #mStatusBarColorWindows}. */
+ private final Rect mStatusBarColorCheckedBounds = new Rect();
+
+ /** The union of checked bounds while fetching {@link #mStatusBarBackgroundWindows}. */
+ private final Rect mStatusBarBackgroundCheckedBounds = new Rect();
+
+ // What we last reported to input dispatcher about whether the focused window is fullscreen.
private boolean mLastFocusIsFullscreen = false;
// If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
@@ -333,19 +358,15 @@ public class DisplayPolicy {
private static final Rect sTmpRect = new Rect();
private static final Rect sTmpNavFrame = new Rect();
private static final Rect sTmpStatusFrame = new Rect();
+ private static final Rect sTmpDecorFrame = new Rect();
private static final Rect sTmpScreenDecorFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
private static final Rect sTmpDisplayFrameBounds = new Rect();
private WindowState mTopFullscreenOpaqueWindowState;
- private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
- private WindowState mTopDockedOpaqueWindowState;
- private WindowState mTopDockedOpaqueOrDimmingWindowState;
private boolean mTopIsFullscreen;
private boolean mForceStatusBar;
private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
- private boolean mForcingShowNavBar;
- private int mForcingShowNavBarLayer;
private boolean mForceShowSystemBars;
private boolean mShowingDream;
@@ -430,8 +451,10 @@ public class DisplayPolicy {
final int displayId = displayContent.getDisplayId();
- mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
- mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
+ mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
+ }
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -613,6 +636,8 @@ public class DisplayPolicy {
}
};
displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
+ mService.mAtmService.getTransitionController().registerLegacyListener(
+ mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
@@ -1079,7 +1104,9 @@ public class DisplayPolicy {
mStatusBar = win;
final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
(displayFrames, windowState, rect) -> {
- rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+ }
};
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
@@ -1089,18 +1116,22 @@ public class DisplayPolicy {
mNavigationBar = win;
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
-
- // In Gesture Nav, navigation bar frame is larger than frame to
- // calculate inset.
- if (navigationBarPosition(displayFrames.mDisplayWidth,
- displayFrames.mDisplayHeight,
- displayFrames.mRotation) == NAV_BAR_BOTTOM
- && !mNavButtonForcedVisible) {
- sTmpRect.set(inOutFrame);
- sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- inOutFrame.top = sTmpRect.bottom
- - getNavigationBarHeight(displayFrames.mRotation,
- mDisplayContent.getConfiguration().uiMode);
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ inOutFrame.inset(windowState.getLayoutingAttrs(
+ displayFrames.mRotation).providedInternalInsets);
+ } else {
+ // In Gesture Nav, navigation bar frame is larger than frame to
+ // calculate inset.
+ if (navigationBarPosition(displayFrames.mDisplayWidth,
+ displayFrames.mDisplayHeight,
+ displayFrames.mRotation) == NAV_BAR_BOTTOM
+ && !mNavButtonForcedVisible) {
+ sTmpRect.set(inOutFrame);
+ sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+ inOutFrame.top = sTmpRect.bottom
+ - getNavigationBarHeight(displayFrames.mRotation,
+ mDisplayContent.getConfiguration().uiMode);
+ }
}
},
@@ -1161,7 +1192,14 @@ public class DisplayPolicy {
mExtraNavBarAltPosition = getAltBarPosition(attrs);
break;
}
- mDisplayContent.setInsetProvider(insetsType, win, null);
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ mDisplayContent.setInsetProvider(insetsType, win, null);
+ } else {
+ mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
+ windowState, inOutFrame) -> inOutFrame.inset(
+ windowState.getLayoutingAttrs(displayFrames.mRotation)
+ .providedInternalInsets));
+ }
}
}
break;
@@ -1252,8 +1290,17 @@ public class DisplayPolicy {
}
private int getStatusBarHeight(DisplayFrames displayFrames) {
- return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
- displayFrames.mDisplayCutoutSafe.top);
+ int statusBarHeight;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mStatusBar != null) {
+ statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height;
+ } else {
+ statusBarHeight = 0;
+ }
+ } else {
+ statusBarHeight = mStatusBarHeightForRotation[displayFrames.mRotation];
+ }
+ return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top);
}
WindowState getStatusBar() {
@@ -1388,7 +1435,7 @@ public class DisplayPolicy {
/**
* @return true if the system bars are forced to stay visible
*/
- public boolean areSystemBarsForcedShownLw(WindowState windowState) {
+ public boolean areSystemBarsForcedShownLw() {
return mForceShowSystemBars;
}
@@ -1423,13 +1470,30 @@ public class DisplayPolicy {
WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames,
Consumer<Rect> layout) {
win.setSimulatedWindowFrames(simulatedWindowFrames);
+ final int requestedHeight = win.mRequestedHeight;
+ final int requestedWidth = win.mRequestedWidth;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ // Without a full layout process, in order to layout the system bars correctly, we need
+ // to set the requested size and the initial display frames to the window.
+ WindowManager.LayoutParams params = win.getLayoutingAttrs(displayFrames.mRotation);
+ win.setRequestedSize(params.width, params.height);
+ sTmpDecorFrame.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
+ simulatedWindowFrames.setFrames(sTmpDecorFrame /* parentFrame */,
+ sTmpDecorFrame /* displayFrame */);
+ simulatedWindowFrames.mIsSimulatingDecorWindow = true;
+ }
final Rect contentFrame = new Rect();
try {
layout.accept(contentFrame);
} finally {
win.setSimulatedWindowFrames(null);
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ win.setRequestedSize(requestedWidth, requestedHeight);
+ }
+ }
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ contentFrames.put(win.mAttrs.type, contentFrame);
}
- contentFrames.put(win.mAttrs.type, contentFrame);
mDisplayContent.getInsetsStateController().computeSimulatedState(
win, displayFrames, simulatedWindowFrames);
}
@@ -1440,15 +1504,31 @@ public class DisplayPolicy {
* some temporal states, but doesn't change the window frames used to show on screen.
*/
void simulateLayoutDisplay(DisplayFrames displayFrames, SparseArray<Rect> barContentFrames) {
- final WindowFrames simulatedWindowFrames = new WindowFrames();
if (mNavigationBar != null) {
- simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
- barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
- contentFrame));
+ final WindowFrames simulatedWindowFrames = new WindowFrames();
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
+ barContentFrames,
+ contentFrame -> simulateLayoutForContentFrame(displayFrames,
+ mNavigationBar, contentFrame));
+ } else {
+ simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
+ barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
+ contentFrame));
+ }
}
if (mStatusBar != null) {
- simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
- barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame));
+ final WindowFrames simulatedWindowFrames = new WindowFrames();
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
+ barContentFrames,
+ contentFrame -> simulateLayoutForContentFrame(displayFrames,
+ mStatusBar, contentFrame));
+ } else {
+ simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
+ barContentFrames,
+ contentFrame -> layoutStatusBar(displayFrames, contentFrame));
+ }
}
}
@@ -1467,7 +1547,7 @@ public class DisplayPolicy {
windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
sTmpStatusFrame /* displayFrame */);
// Let the status bar determine its size.
- mStatusBar.computeFrameAndUpdateSourceFrame();
+ mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames);
// For layout, the status bar is always at the top with our fixed height.
int statusBarBottom = displayFrames.mUnrestricted.top
@@ -1518,18 +1598,18 @@ public class DisplayPolicy {
} else if (navBarPosition == NAV_BAR_RIGHT) {
// Landscape screen; nav bar goes to the right.
navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right)
- - getNavigationBarWidth(rotation, uiMode);
+ - getNavigationBarWidth(rotation, uiMode, navBarPosition);
} else if (navBarPosition == NAV_BAR_LEFT) {
// Seascape screen; nav bar goes to the left.
navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left)
- + getNavigationBarWidth(rotation, uiMode);
+ + getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
// Compute the final frame.
final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames();
windowFrames.setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */);
- mNavigationBar.computeFrameAndUpdateSourceFrame();
+ mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames);
sTmpRect.set(windowFrames.mFrame);
sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
contentFrame.set(sTmpRect);
@@ -1538,6 +1618,16 @@ public class DisplayPolicy {
return navBarPosition;
}
+ private void simulateLayoutForContentFrame(DisplayFrames displayFrames, WindowState win,
+ Rect simulatedContentFrame) {
+ layoutWindowLw(win, null /* attached */, displayFrames);
+ final Rect contentFrame = sTmpRect;
+ contentFrame.set(win.getLayoutingWindowFrames().mFrame);
+ // Excluding the display cutout before set to the simulated content frame.
+ contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
+ simulatedContentFrame.set(contentFrame);
+ }
+
private boolean canReceiveInput(WindowState win) {
boolean notFocusable =
(win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
@@ -1559,12 +1649,12 @@ public class DisplayPolicy {
* @param displayFrames The display frames.
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
- if (win == mNavigationBar) {
+ if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) {
mNavigationBarPosition = layoutNavigationBar(displayFrames,
mBarContentFrames.get(TYPE_NAVIGATION_BAR));
return;
}
- if ((win == mStatusBar && !canReceiveInput(win))) {
+ if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) {
layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
@@ -1572,7 +1662,7 @@ public class DisplayPolicy {
// Skip layout of the window when in transition to pip mode.
return;
}
- final WindowManager.LayoutParams attrs = win.getAttrs();
+ final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
final int type = attrs.type;
final int fl = attrs.flags;
@@ -1580,7 +1670,7 @@ public class DisplayPolicy {
final int sim = attrs.softInputMode;
displayFrames = win.getDisplayFrames(displayFrames);
- final WindowFrames windowFrames = win.getWindowFrames();
+ final WindowFrames windowFrames = win.getLayoutingWindowFrames();
sTmpLastParentFrame.set(windowFrames.mParentFrame);
final Rect pf = windowFrames.mParentFrame;
@@ -1591,7 +1681,13 @@ public class DisplayPolicy {
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
final InsetsState state = win.getInsetsState();
- computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
+ if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) {
+ // Override the bounds in window token has many side effects. Directly use the display
+ // frame set for the simulated layout for this case.
+ computeWindowBounds(attrs, state, df, df);
+ } else {
+ computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
+ }
if (attached == null) {
pf.set(df);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
@@ -1691,7 +1787,17 @@ public class DisplayPolicy {
windowFrames.setContentChanged(true);
}
- win.computeFrameAndUpdateSourceFrame();
+ win.computeFrameAndUpdateSourceFrame(displayFrames);
+ if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) {
+ if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
+ // Make sure that the zone we're avoiding for the cutout is at least as tall as the
+ // status bar; otherwise fullscreen apps will end up cutting halfway into the status
+ // bar.
+ displayFrames.mDisplayCutoutSafe.top = Math.max(
+ displayFrames.mDisplayCutoutSafe.top,
+ windowFrames.mFrame.bottom);
+ }
+ }
}
WindowState getTopFullscreenOpaqueWindow() {
@@ -1707,12 +1813,13 @@ public class DisplayPolicy {
*/
public void beginPostLayoutPolicyLw() {
mTopFullscreenOpaqueWindowState = null;
- mTopFullscreenOpaqueOrDimmingWindowState = null;
- mTopDockedOpaqueWindowState = null;
- mTopDockedOpaqueOrDimmingWindowState = null;
+ mNavBarColorWindowCandidate = null;
+ mNavBarBackgroundWindow = null;
+ mStatusBarColorWindows.clear();
+ mStatusBarBackgroundWindows.clear();
+ mStatusBarColorCheckedBounds.setEmpty();
+ mStatusBarBackgroundCheckedBounds.setEmpty();
mForceStatusBar = false;
- mForcingShowNavBar = false;
- mForcingShowNavBarLayer = -1;
mAllowLockscreenWhenOn = false;
mShowingDream = false;
@@ -1731,20 +1838,22 @@ public class DisplayPolicy {
final boolean affectsSystemUi = win.canAffectSystemUiFlags();
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
applyKeyguardPolicy(win, imeTarget);
- final int fl = attrs.flags;
- if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
- && attrs.type == TYPE_INPUT_METHOD) {
- mForcingShowNavBar = true;
- mForcingShowNavBarLayer = win.getSurfaceLayer();
+
+ // Check if the freeform window overlaps with the navigation bar area.
+ final boolean isOverlappingWithNavBar = isOverlappingWithNavBar(win, mNavigationBar);
+ if (isOverlappingWithNavBar && !mIsFreeformWindowOverlappingWithNavBar
+ && win.inFreeformWindowingMode()) {
+ mIsFreeformWindowOverlappingWithNavBar = true;
+ }
+
+ if (!affectsSystemUi) {
+ return;
}
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
- final int windowingMode = win.getWindowingMode();
- final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
- windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
+ if (mTopFullscreenOpaqueWindowState == null) {
+ final int fl = attrs.flags;
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
mForceStatusBar = true;
}
@@ -1757,68 +1866,60 @@ public class DisplayPolicy {
}
}
- // For app windows that are not attached, we decide if all windows in the app they
- // represent should be hidden or if we should hide the lockscreen. For attached app
- // windows we defer the decision to the window it is attached to.
- if (appWindow && attached == null) {
- if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
- mTopFullscreenOpaqueWindowState = win;
- if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
- if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
- mAllowLockscreenWhenOn = true;
- }
- }
+ if (appWindow && attached == null && attrs.isFullscreen()
+ && (fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+ mAllowLockscreenWhenOn = true;
}
}
- // Voice interaction overrides both top fullscreen and top docked.
- if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION && attrs.isFullscreen()) {
+ // Check the windows that overlap with system bars to determine system bars' appearance.
+ if ((appWindow && attached == null && attrs.isFullscreen())
+ || attrs.type == TYPE_VOICE_INTERACTION) {
+ // Record the top-fullscreen-app-window which will be used to determine system UI
+ // controlling window.
if (mTopFullscreenOpaqueWindowState == null) {
mTopFullscreenOpaqueWindowState = win;
- if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
}
- if (mTopDockedOpaqueWindowState == null) {
- mTopDockedOpaqueWindowState = win;
- if (mTopDockedOpaqueOrDimmingWindowState == null) {
- mTopDockedOpaqueOrDimmingWindowState = win;
+
+ // Cache app windows that is overlapping with the status bar to determine appearance
+ // of status bar.
+ if (mStatusBar != null
+ && sTmpRect.setIntersect(win.getFrame(), mStatusBar.getFrame())
+ && !mStatusBarBackgroundCheckedBounds.contains(sTmpRect)) {
+ mStatusBarBackgroundWindows.add(win);
+ mStatusBarBackgroundCheckedBounds.union(sTmpRect);
+ if (!mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+ mStatusBarColorWindows.add(win);
+ mStatusBarColorCheckedBounds.union(sTmpRect);
}
}
- }
- // Keep track of the window if it's dimming but not necessarily fullscreen.
- if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
- && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
-
- // We need to keep track of the top "fullscreen" opaque window for the docked root task
- // separately, because both the "real fullscreen" opaque window and the one for the docked
- // root task can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
- if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
- && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mTopDockedOpaqueWindowState = win;
- if (mTopDockedOpaqueOrDimmingWindowState == null) {
- mTopDockedOpaqueOrDimmingWindowState = win;
+ // Cache app window that overlaps with the navigation bar area to determine opacity
+ // and appearance of the navigation bar. We only need to cache one window because
+ // there should be only one overlapping window if it's not in gesture navigation
+ // mode; if it's in gesture navigation mode, the navigation bar will be
+ // NAV_BAR_FORCE_TRANSPARENT and its appearance won't be decided by overlapping
+ // windows.
+ if (isOverlappingWithNavBar) {
+ if (mNavBarColorWindowCandidate == null) {
+ mNavBarColorWindowCandidate = win;
+ }
+ if (mNavBarBackgroundWindow == null) {
+ mNavBarBackgroundWindow = win;
+ }
+ }
+ } else if (win.isDimming()) {
+ // For dimming window whose host bounds is overlapping with system bars, it can be
+ // used to determine colors but not opacity of system bars.
+ if (mStatusBar != null
+ && sTmpRect.setIntersect(win.getBounds(), mStatusBar.getFrame())
+ && !mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+ mStatusBarColorWindows.add(win);
+ mStatusBarColorCheckedBounds.union(sTmpRect);
+ }
+ if (isOverlappingWithNavBar && mNavigationBar == null) {
+ mNavBarColorWindowCandidate = win;
}
- }
-
- // Check if the freeform window overlaps with the navigation bar area.
- final WindowState navBarWin = hasNavigationBar() ? mNavigationBar : null;
- if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode()
- && isOverlappingWithNavBar(win, navBarWin)) {
- mIsFreeformWindowOverlappingWithNavBar = true;
- }
-
- // Also keep track of any windows that are dimming but not necessarily fullscreen in the
- // docked root task.
- if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
- && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mTopDockedOpaqueOrDimmingWindowState = win;
}
}
@@ -2120,18 +2221,47 @@ public class DisplayPolicy {
return mUiContext;
}
- private int getNavigationBarWidth(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarWidthForRotationInCarMode[rotation];
+ private int getNavigationBarWidth(int rotation, int uiMode, int position) {
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ LayoutParams lp = mNavigationBar.mAttrs;
+ if (lp.paramsForRotation != null
+ && lp.paramsForRotation.length == 4
+ && lp.paramsForRotation[rotation] != null) {
+ lp = lp.paramsForRotation[rotation];
+ }
+ if (position == NAV_BAR_LEFT) {
+ if (lp.width > lp.providedInternalInsets.right) {
+ return lp.width - lp.providedInternalInsets.right;
+ } else {
+ return 0;
+ }
+ } else if (position == NAV_BAR_RIGHT) {
+ if (lp.width > lp.providedInternalInsets.left) {
+ return lp.width - lp.providedInternalInsets.left;
+ } else {
+ return 0;
+ }
+ }
+ return lp.width;
} else {
- return mNavigationBarWidthForRotationDefault[rotation];
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarWidthForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarWidthForRotationDefault[rotation];
+ }
}
}
void notifyDisplayReady() {
mHandler.post(() -> {
final int displayId = getDisplayId();
- getStatusBarManagerInternal().onDisplayReady(displayId);
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.onDisplayReady(displayId);
+ }
final WallpaperManagerInternal wpMgr = LocalServices
.getService(WallpaperManagerInternal.class);
if (wpMgr != null) {
@@ -2151,7 +2281,7 @@ public class DisplayPolicy {
if (hasNavigationBar()) {
final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
- width -= getNavigationBarWidth(rotation, uiMode);
+ width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
}
if (displayCutout != null) {
@@ -2161,10 +2291,21 @@ public class DisplayPolicy {
}
private int getNavigationBarHeight(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
+ if (lp.height < lp.providedInternalInsets.top) {
+ return 0;
+ }
+ return lp.height - lp.providedInternalInsets.top;
} else {
- return mNavigationBarHeightForRotationDefault[rotation];
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarHeightForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarHeightForRotationDefault[rotation];
+ }
}
}
@@ -2181,10 +2322,17 @@ public class DisplayPolicy {
* @return navigation bar frame height
*/
private int getNavigationBarFrameHeight(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ return mNavigationBar.mAttrs.height;
} else {
- return mNavigationBarFrameHeightForRotationDefault[rotation];
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarHeightForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarFrameHeightForRotationDefault[rotation];
+ }
}
}
@@ -2305,9 +2453,9 @@ public class DisplayPolicy {
if (position == NAV_BAR_BOTTOM) {
outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);
} else if (position == NAV_BAR_RIGHT) {
- outInsets.right = getNavigationBarWidth(displayRotation, uiMode);
+ outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);
} else if (position == NAV_BAR_LEFT) {
- outInsets.left = getNavigationBarWidth(displayRotation, uiMode);
+ outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
}
}
@@ -2333,6 +2481,17 @@ public class DisplayPolicy {
@NavigationBarPosition
int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+ if (INSETS_LAYOUT_GENERALIZATION && mNavigationBar != null) {
+ final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity;
+ switch (gravity) {
+ case Gravity.LEFT:
+ return NAV_BAR_LEFT;
+ case Gravity.RIGHT:
+ return NAV_BAR_RIGHT;
+ default:
+ return NAV_BAR_BOTTOM;
+ }
+ }
if (navigationBarCanMove() && displayWidth > displayHeight) {
if (displayRotation == Surface.ROTATION_270) {
return NAV_BAR_LEFT;
@@ -2490,45 +2649,34 @@ public class DisplayPolicy {
final WindowState win = winCandidate;
mSystemUiControllingWindow = win;
- mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
-
- final boolean inSplitScreen =
- mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
- if (inSplitScreen) {
- mService.getRootTaskBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- mDockedRootTaskBounds);
- } else {
- mDockedRootTaskBounds.setEmpty();
- }
- mService.getRootTaskBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_UNDEFINED, mNonDockedRootTaskBounds);
- final int fullscreenAppearance = getStatusBarAppearance(mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedAppearance = getStatusBarAppearance(mTopDockedOpaqueWindowState,
- mTopDockedOpaqueOrDimmingWindowState);
final int disableFlags = win.getDisableFlags();
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
- final WindowState navColorWin = chooseNavigationColorWindowLw(
- mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
- final int appearance = updateLightNavigationBarLw(
- win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState,
- mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
+ final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
+ navColorWin) | opaqueAppearance;
final int behavior = win.mAttrs.insetsFlags.behavior;
final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
|| !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
+
+ final AppearanceRegion[] appearanceRegions =
+ new AppearanceRegion[mStatusBarColorWindows.size()];
+ for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
+ final WindowState windowState = mStatusBarColorWindows.get(i);
+ appearanceRegions[i] = new AppearanceRegion(
+ getStatusBarAppearance(windowState, windowState),
+ new Rect(windowState.getFrame()));
+ }
+
if (mLastDisableFlags == disableFlags
&& mLastAppearance == appearance
- && mLastFullscreenAppearance == fullscreenAppearance
- && mLastDockedAppearance == dockedAppearance
&& mLastBehavior == behavior
+ && mRequestedState.equals(win.getRequestedState())
+ && Objects.equals(mFocusedApp, win.mAttrs.packageName)
&& mLastFocusIsFullscreen == isFullscreen
- && mLastNonDockedRootTaskBounds.equals(mNonDockedRootTaskBounds)
- && mLastDockedRootTaskBounds.equals(mDockedRootTaskBounds)) {
+ && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
return false;
}
if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
@@ -2538,28 +2686,19 @@ public class DisplayPolicy {
}
mLastDisableFlags = disableFlags;
mLastAppearance = appearance;
- mLastFullscreenAppearance = fullscreenAppearance;
- mLastDockedAppearance = dockedAppearance;
mLastBehavior = behavior;
+ mRequestedState.set(win.getRequestedState(), true /* copySources */);
+ mFocusedApp = win.mAttrs.packageName;
mLastFocusIsFullscreen = isFullscreen;
- mLastNonDockedRootTaskBounds.set(mNonDockedRootTaskBounds);
- mLastDockedRootTaskBounds.set(mDockedRootTaskBounds);
- final Rect fullscreenRootTaskBounds = new Rect(mNonDockedRootTaskBounds);
- final Rect dockedRootTaskBounds = new Rect(mDockedRootTaskBounds);
- final AppearanceRegion[] appearanceRegions = inSplitScreen
- ? new AppearanceRegion[]{
- new AppearanceRegion(fullscreenAppearance, fullscreenRootTaskBounds),
- new AppearanceRegion(dockedAppearance, dockedRootTaskBounds)}
- : new AppearanceRegion[]{
- new AppearanceRegion(fullscreenAppearance, fullscreenRootTaskBounds)};
- String cause = win.toString();
+ mLastStatusBarAppearanceRegions = appearanceRegions;
+ final String cause = win.toString();
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
final int displayId = getDisplayId();
statusBar.setDisableFlags(displayId, disableFlags, cause);
statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- isNavbarColorManagedByIme, behavior, isFullscreen);
+ isNavbarColorManagedByIme, behavior, mRequestedState, mFocusedApp);
}
});
@@ -2576,8 +2715,7 @@ public class DisplayPolicy {
@VisibleForTesting
@Nullable
- static WindowState chooseNavigationColorWindowLw(WindowState opaque,
- WindowState opaqueOrDimming, WindowState imeWindow,
+ static WindowState chooseNavigationColorWindowLw(WindowState candidate, WindowState imeWindow,
@NavigationBarPosition int navBarPosition) {
// If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
// window can be navigation color window.
@@ -2586,71 +2724,59 @@ public class DisplayPolicy {
&& navBarPosition == NAV_BAR_BOTTOM
&& (imeWindow.mAttrs.flags
& WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-
- if (opaque != null && opaqueOrDimming == opaque) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
- // unless IME window is also eligible, since currently the IME window is always show
- // above the opaque fullscreen app window, regardless of the IME target window.
- // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
- return imeWindowCanNavColorWindow ? imeWindow : opaque;
- }
-
- if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
- // No dimming window is involved. Determine the result only with the IME window.
- return imeWindowCanNavColorWindow ? imeWindow : null;
- }
-
if (!imeWindowCanNavColorWindow) {
- // No IME window is involved. Determine the result only with opaqueOrDimming.
- return opaqueOrDimming;
+ // No IME window is involved. Determine the result only with candidate window.
+ return candidate;
}
- // The IME window and the dimming window are competing. Check if the dimming window can be
- // IME target or not.
- if (LayoutParams.mayUseInputMethod(opaqueOrDimming.mAttrs.flags)) {
- // The IME window is above the dimming window.
- return imeWindow;
- } else {
- // The dimming window is above the IME window.
- return opaqueOrDimming;
+ if (candidate != null && candidate.isDimming()) {
+ // The IME window and the dimming window are competing. Check if the dimming window can
+ // be IME target or not.
+ if (LayoutParams.mayUseInputMethod(candidate.mAttrs.flags)) {
+ // The IME window is above the dimming window.
+ return imeWindow;
+ } else {
+ // The dimming window is above the IME window.
+ return candidate;
+ }
}
+
+ return imeWindow;
}
@VisibleForTesting
- int updateLightNavigationBarLw(int appearance, WindowState opaque,
- WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
-
- if (navColorWin != null) {
- if (navColorWin == imeWindow || navColorWin == opaque) {
- // Respect the light flag.
- appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
- appearance |= navColorWin.mAttrs.insetsFlags.appearance
- & APPEARANCE_LIGHT_NAVIGATION_BARS;
- } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
- // Clear the light flag for dimming window.
- appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
- }
- }
- if (!isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) {
+ int updateLightNavigationBarLw(int appearance, WindowState navColorWin) {
+ if (navColorWin == null || navColorWin.isDimming()
+ || !isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) {
+ // Clear the light flag while not allowed.
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ return appearance;
}
+
+ // Respect the light flag of navigation color window.
+ appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ appearance |= navColorWin.mAttrs.insetsFlags.appearance
+ & APPEARANCE_LIGHT_NAVIGATION_BARS;
return appearance;
}
private int updateSystemBarsLw(WindowState win, int disableFlags) {
- final boolean dockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
-
- // We need to force system bars when the docked root task is visible, when the freeform
- // root task is focused but also when we are resizing for the transitions when docked
- // root task visibility changes.
- mForceShowSystemBars = dockedRootTaskVisible || win.inFreeformWindowingMode() || resizing;
+ final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final boolean multiWindowTaskVisible =
+ defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
+ final boolean freeformRootTaskVisible =
+ defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
+
+ // We need to force showing system bars when the multi-window or freeform root task is
+ // visible.
+ mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
-
appearance = configureStatusBarOpacity(appearance);
- appearance = configureNavBarOpacity(appearance, dockedRootTaskVisible, resizing);
+ appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible,
+ freeformRootTaskVisible);
final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
final long now = SystemClock.uptimeMillis();
@@ -2696,7 +2822,24 @@ public class DisplayPolicy {
private Rect getBarContentFrameForWindow(WindowState win, int windowType) {
final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType);
- return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType);
+ if (rotatedBarFrame != null) {
+ return rotatedBarFrame;
+ }
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ return mBarContentFrames.get(windowType);
+ }
+ // We only need a window specific information for the fixed rotation, use raw insets state
+ // for all other cases.
+ InsetsState insetsState = mDisplayContent.getInsetsStateController().getRawInsetsState();
+ final Rect tmpRect = new Rect();
+ if (windowType == TYPE_NAVIGATION_BAR) {
+ tmpRect.set(insetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).getFrame());
+ }
+ if (windowType == TYPE_STATUS_BAR) {
+ tmpRect.set(insetsState.getSource(InsetsState.ITYPE_STATUS_BAR).getFrame());
+ }
+ tmpRect.intersect(mDisplayContent.mDisplayFrames.mDisplayCutoutSafe);
+ return tmpRect;
}
/**
@@ -2731,17 +2874,19 @@ public class DisplayPolicy {
/** @return the current visibility flags with the status bar opacity related flags toggled. */
private int configureStatusBarOpacity(int appearance) {
- final boolean fullscreenDrawsBackground =
- drawsBarBackground(mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsBackground =
- drawsBarBackground(mTopDockedOpaqueWindowState);
+ boolean drawBackground = true;
+ boolean isFullyTransparentAllowed = true;
+ for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+ final WindowState window = mStatusBarBackgroundWindows.get(i);
+ drawBackground &= drawsBarBackground(window);
+ isFullyTransparentAllowed &= isFullyTransparentAllowed(window, TYPE_STATUS_BAR);
+ }
- if (fullscreenDrawsBackground && dockedDrawsBackground) {
+ if (drawBackground) {
appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
}
- if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_STATUS_BAR)
- || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_STATUS_BAR)) {
+ if (!isFullyTransparentAllowed) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
}
@@ -2752,53 +2897,35 @@ public class DisplayPolicy {
* @return the current visibility flags with the nav-bar opacity related flags toggled based
* on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
*/
- private int configureNavBarOpacity(int appearance, boolean dockedRootTaskVisible,
- boolean isDockedDividerResizing) {
- final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- final boolean fullscreenDrawsBackground =
- drawsBarBackground(mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsBackground =
- drawsBarBackground(mTopDockedOpaqueWindowState);
+ private int configureNavBarOpacity(int appearance, boolean multiWindowTaskVisible,
+ boolean freeformRootTaskVisible) {
+ final boolean drawBackground = drawsBarBackground(mNavBarBackgroundWindow);
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
- if (fullscreenDrawsBackground && dockedDrawsBackground) {
+ if (drawBackground) {
appearance = clearNavBarOpaqueFlag(appearance);
- } else if (dockedRootTaskVisible) {
- appearance = setNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
- if (dockedRootTaskVisible || freeformRootTaskVisible || isDockedDividerResizing) {
+ if (multiWindowTaskVisible || freeformRootTaskVisible) {
if (mIsFreeformWindowOverlappingWithNavBar) {
appearance = clearNavBarOpaqueFlag(appearance);
- } else {
- appearance = setNavBarOpaqueFlag(appearance);
}
- } else if (fullscreenDrawsBackground) {
+ } else if (drawBackground) {
appearance = clearNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
- if (isDockedDividerResizing) {
- appearance = setNavBarOpaqueFlag(appearance);
- } else if (freeformRootTaskVisible) {
+ if (freeformRootTaskVisible) {
appearance = clearNavBarOpaqueFlag(appearance);
- } else {
- appearance = setNavBarOpaqueFlag(appearance);
}
}
- if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_NAVIGATION_BAR)
- || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_NAVIGATION_BAR)) {
+ if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, TYPE_NAVIGATION_BAR)) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
}
return appearance;
}
- private int setNavBarOpaqueFlag(int appearance) {
- return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS;
- }
-
private int clearNavBarOpaqueFlag(int appearance) {
return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS;
}
@@ -2899,6 +3026,7 @@ public class DisplayPolicy {
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.println("DisplayPolicy");
prefix += " ";
+ final String prefixInner = prefix + " ";
pw.print(prefix);
pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer);
pw.print(" mDeskDockEnablesAccelerometer=");
@@ -2966,14 +3094,27 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
pw.println(mTopFullscreenOpaqueWindowState);
}
- if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
- pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
- pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
+ if (mNavBarColorWindowCandidate != null) {
+ pw.print(prefix); pw.print("mNavBarColorWindowCandidate=");
+ pw.println(mNavBarColorWindowCandidate);
+ }
+ if (mNavBarBackgroundWindow != null) {
+ pw.print(prefix); pw.print("mNavBarBackgroundWindow=");
+ pw.println(mNavBarBackgroundWindow);
}
- if (mForcingShowNavBar) {
- pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar);
- pw.print(prefix); pw.print("mForcingShowNavBarLayer=");
- pw.println(mForcingShowNavBarLayer);
+ if (!mStatusBarColorWindows.isEmpty()) {
+ pw.print(prefix); pw.println("mStatusBarColorWindows=");
+ for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mStatusBarColorWindows.get(i);
+ pw.print(prefixInner); pw.println(win);
+ }
+ }
+ if (!mStatusBarBackgroundWindows.isEmpty()) {
+ pw.print(prefix); pw.println("mStatusBarBackgroundWindows=");
+ for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mStatusBarBackgroundWindows.get(i);
+ pw.print(prefixInner); pw.println(win);
+ }
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
@@ -3055,13 +3196,17 @@ public class DisplayPolicy {
}
@VisibleForTesting
- static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
+ static boolean isOverlappingWithNavBar(@NonNull WindowState targetWindow,
+ WindowState navBarWindow) {
if (navBarWindow == null || !navBarWindow.isVisible()
|| targetWindow.mActivityRecord == null || !targetWindow.isVisible()) {
return false;
}
- return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame());
+ // When the window is dimming means it's requesting dim layer to its host container, so
+ // checking whether it's overlapping with navigation bar by its container's bounds.
+ return Rect.intersects(targetWindow.isDimming()
+ ? targetWindow.getBounds() : targetWindow.getFrame(), navBarWindow.getFrame());
}
/**
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 316c20ba5c47..2f0d703bb200 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -24,22 +24,22 @@ import android.util.Slog;
/** Helper class to ensure activities are in the right visible state for a container. */
class EnsureActivitiesVisibleHelper {
- private final Task mTask;
+ private final TaskFragment mTaskFragment;
private ActivityRecord mTop;
private ActivityRecord mStarting;
private boolean mAboveTop;
private boolean mContainerShouldBeVisible;
- private boolean mBehindFullscreenActivity;
+ private boolean mBehindFullyOccludedContainer;
private int mConfigChanges;
private boolean mPreserveWindows;
private boolean mNotifyClients;
- EnsureActivitiesVisibleHelper(Task container) {
- mTask = container;
+ EnsureActivitiesVisibleHelper(TaskFragment container) {
+ mTaskFragment = container;
}
/**
- * Update all attributes except {@link mTask} to use in subsequent calculations.
+ * Update all attributes except {@link mTaskFragment} to use in subsequent calculations.
*
* @param starting The activity that is being started
* @param configChanges Parts of the configuration that changed for this activity for evaluating
@@ -51,12 +51,12 @@ class EnsureActivitiesVisibleHelper {
void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
mStarting = starting;
- mTop = mTask.topRunningActivity();
+ mTop = mTaskFragment.topRunningActivity();
// If the top activity is not fullscreen, then we need to make sure any activities under it
// are now visible.
mAboveTop = mTop != null;
- mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting);
- mBehindFullscreenActivity = !mContainerShouldBeVisible;
+ mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
+ mBehindFullyOccludedContainer = !mContainerShouldBeVisible;
mConfigChanges = configChanges;
mPreserveWindows = preserveWindows;
mNotifyClients = notifyClients;
@@ -85,22 +85,35 @@ class EnsureActivitiesVisibleHelper {
Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
+ " configChanges=0x" + Integer.toHexString(configChanges));
}
- if (mTop != null) {
- mTask.checkTranslucentActivityWaiting(mTop);
+ if (mTop != null && mTaskFragment.asTask() != null) {
+ // TODO(14709632): Check if this needed to be implemented in TaskFragment.
+ mTaskFragment.asTask().checkTranslucentActivityWaiting(mTop);
}
// We should not resume activities that being launched behind because these
// activities are actually behind other fullscreen activities, but still required
// to be visible (such as performing Recents animation).
final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
- && mTask.isTopActivityFocusable()
- && (starting == null || !starting.isDescendantOf(mTask));
-
- mTask.forAllActivities(a -> {
- setActivityVisibilityState(a, starting, resumeTopActivity);
- });
- if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
- mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+ && mTaskFragment.isTopActivityFocusable()
+ && (starting == null || !starting.isDescendantOf(mTaskFragment));
+
+ for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mTaskFragment.mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ final TaskFragment childTaskFragment = child.asTaskFragment();
+ childTaskFragment.updateActivityVisibilities(starting, configChanges,
+ preserveWindows, notifyClients);
+ mBehindFullyOccludedContainer = childTaskFragment.getBounds().equals(
+ mTaskFragment.getBounds());
+ if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
+ mAboveTop = false;
+ }
+ } else if (child.asActivityRecord() != null) {
+ setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
+ }
+ }
+ if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
}
}
@@ -112,7 +125,7 @@ class EnsureActivitiesVisibleHelper {
}
mAboveTop = false;
- r.updateVisibilityIgnoringKeyguard(mBehindFullscreenActivity);
+ r.updateVisibilityIgnoringKeyguard(mBehindFullyOccludedContainer);
final boolean reallyVisible = r.shouldBeVisibleUnchecked();
// Check whether activity should be visible without Keyguard influence
@@ -122,11 +135,11 @@ class EnsureActivitiesVisibleHelper {
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " containerVisible=" + mContainerShouldBeVisible
- + " behindFullscreen=" + mBehindFullscreenActivity);
+ + " behindFullyOccluded=" + mBehindFullyOccludedContainer);
}
- mBehindFullscreenActivity = true;
+ mBehindFullyOccludedContainer = true;
} else {
- mBehindFullscreenActivity = false;
+ mBehindFullyOccludedContainer = false;
}
}
@@ -173,24 +186,25 @@ class EnsureActivitiesVisibleHelper {
Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ " finishing=" + r.finishing + " state=" + r.getState()
+ " containerShouldBeVisible=" + mContainerShouldBeVisible
- + " behindFullscreenActivity=" + mBehindFullscreenActivity
+ + " behindFullyOccludedContainer=" + mBehindFullyOccludedContainer
+ " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
}
r.makeInvisible();
}
- if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
+ if (!mBehindFullyOccludedContainer && mTaskFragment.isActivityTypeHome()
+ && r.isRootOfTask()) {
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "Home task: at " + mTask
+ Slog.v(TAG_VISIBILITY, "Home task: at " + mTaskFragment
+ " containerShouldBeVisible=" + mContainerShouldBeVisible
- + " behindFullscreenActivity=" + mBehindFullscreenActivity);
+ + " behindOccludedParentContainer=" + mBehindFullyOccludedContainer);
}
// No other task in the root home task should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
// them. However, when they don't have the wallpaper behind them, we want to
// show activities in the next application root task behind them vs. another
// task in the root home task like recents.
- mBehindFullscreenActivity = true;
+ mBehindFullyOccludedContainer = true;
}
}
@@ -219,7 +233,8 @@ class EnsureActivitiesVisibleHelper {
r.setVisibility(true);
}
if (r != starting) {
- mTask.mTaskSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
+ mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume,
+ true /* checkConfig */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index da47328691c0..4f6a693b8c3f 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_D
import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
@@ -90,6 +91,16 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
onSourceChanged();
}
+ @Override
+ void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+ if (target != null && target.getWindow() != null) {
+ // ime control target could be a different window.
+ // Refer WindowState#getImeControlTarget().
+ target = target.getWindow().getImeControlTarget();
+ }
+ super.updateControlForTarget(target, force);
+ }
+
private void onSourceChanged() {
if (mLastSource.equals(mSource)) {
return;
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 747d3652e150..f3b9cdfd39e0 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
@@ -420,7 +421,7 @@ public class ImmersiveModeConfirmation {
}
final Bundle options = new Bundle();
- options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
+ options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
return options;
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8c781a13f7db..eb5ab83756ef 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
@@ -617,7 +616,6 @@ final class InputMonitor {
inputWindowHandle.setScaleFactor(1f);
inputWindowHandle.setLayoutParamsFlags(
FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE);
- inputWindowHandle.setPortalToDisplayId(INVALID_DISPLAY);
inputWindowHandle.clearTouchableRegion();
inputWindowHandle.setTouchableRegionCrop(null);
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 7a4d13c2d697..b4e11ff77cb3 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -223,14 +223,6 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- void setPortalToDisplayId(int displayId) {
- if (mHandle.portalToDisplayId == displayId) {
- return;
- }
- mHandle.portalToDisplayId = displayId;
- mChanged = true;
- }
-
void setFrame(int left, int top, int right, int bottom) {
if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right
&& mHandle.frameBottom == bottom) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f2f192686ad5..afffe163f045 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -18,8 +18,6 @@ package com.android.server.wm;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
@@ -55,6 +53,7 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
+import com.android.server.statusbar.StatusBarManagerInternal;
/**
* Policy that implements who gets control over the windows generating insets.
@@ -135,11 +134,8 @@ class InsetsPolicy {
abortTransient();
}
mFocusedWin = focusedWin;
- boolean forceShowsSystemBarsForWindowingMode = forceShowsSystemBarsForWindowingMode();
- InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin,
- forceShowsSystemBarsForWindowingMode);
- InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin,
- forceShowsSystemBarsForWindowingMode);
+ InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin);
+ InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin);
mStateController.onBarControlTargetChanged(statusControlTarget,
getFakeControlTarget(focusedWin, statusControlTarget),
navControlTarget,
@@ -167,8 +163,12 @@ class InsetsPolicy {
changed = true;
}
if (changed) {
- mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
- mShowingTransientTypes.toArray());
+ StatusBarManagerInternal statusBarManagerInternal =
+ mPolicy.getStatusBarManagerInternal();
+ if (statusBarManagerInternal != null) {
+ statusBarManagerInternal.showTransient(
+ mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+ }
updateBarControlTarget(mFocusedWin);
// The leashes can be created while updating bar control target. The surface transaction
@@ -282,9 +282,11 @@ class InsetsPolicy {
abortTypes.add(type);
}
}
- if (abortTypes.size() > 0) {
- mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
- abortTypes.toArray());
+ StatusBarManagerInternal statusBarManagerInternal =
+ mPolicy.getStatusBarManagerInternal();
+ if (abortTypes.size() > 0 && statusBarManagerInternal != null) {
+ statusBarManagerInternal.abortTransient(
+ mDisplayContent.getDisplayId(), abortTypes.toArray());
}
}
}
@@ -294,8 +296,11 @@ class InsetsPolicy {
* updateBarControlTarget(mFocusedWin) after this invocation.
*/
private void abortTransient() {
- mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
- mShowingTransientTypes.toArray());
+ StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal();
+ if (statusBarManagerInternal != null) {
+ statusBarManagerInternal.abortTransient(
+ mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+ }
mShowingTransientTypes.clear();
}
@@ -304,8 +309,7 @@ class InsetsPolicy {
return realControlTarget == mDummyControlTarget ? focused : null;
}
- private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
- boolean forceShowsSystemBarsForWindowingMode) {
+ private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin) {
if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) {
return mDummyControlTarget;
}
@@ -319,10 +323,9 @@ class InsetsPolicy {
focusedWin.mAttrs.packageName);
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (forceShowsSystemBarsForWindowingMode) {
- // Status bar is forcibly shown for the windowing mode which is a steady state.
- // We don't want the client to control the status bar, and we will dispatch the real
- // visibility of status bar to the client.
+ if (mPolicy.areSystemBarsForcedShownLw()) {
+ // Status bar is forcibly shown. We don't want the client to control the status bar, and
+ // we will dispatch the real visibility of status bar to the client.
return null;
}
if (forceShowsStatusBarTransiently()) {
@@ -350,8 +353,7 @@ class InsetsPolicy {
&& !win.inMultiWindowMode();
}
- private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
- boolean forceShowsSystemBarsForWindowingMode) {
+ private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
final WindowState imeWin = mDisplayContent.mInputMethodWindow;
if (imeWin != null && imeWin.isVisible()) {
// Force showing navigation bar while IME is visible.
@@ -369,10 +371,9 @@ class InsetsPolicy {
focusedWin.mAttrs.packageName);
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (forceShowsSystemBarsForWindowingMode) {
- // Navigation bar is forcibly shown for the windowing mode which is a steady state.
- // We don't want the client to control the navigation bar, and we will dispatch the real
- // visibility of navigation bar to the client.
+ if (mPolicy.areSystemBarsForcedShownLw()) {
+ // Navigation bar is forcibly shown. We don't want the client to control the navigation
+ // bar, and we will dispatch the real visibility of navigation bar to the client.
return null;
}
if (forceShowsNavigationBarTransiently()) {
@@ -417,19 +418,6 @@ class InsetsPolicy {
&& (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
}
- private boolean forceShowsSystemBarsForWindowingMode() {
- final boolean isDockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final boolean isFreeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
-
- // We need to force system bars when the docked root task is visible, when the freeform
- // root task is visible but also when we are resizing for the transitions when docked
- // root task visibility changes.
- return isDockedRootTaskVisible || isFreeformRootTaskVisible || isResizing;
- }
-
@VisibleForTesting
void startAnimation(boolean show, Runnable callback) {
int typesReady = 0;
@@ -474,8 +462,12 @@ class InsetsPolicy {
final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
if (mState != state) {
mState = state;
- mPolicy.getStatusBarManagerInternal().setWindowState(
- mDisplayContent.getDisplayId(), mId, state);
+ StatusBarManagerInternal statusBarManagerInternal =
+ mPolicy.getStatusBarManagerInternal();
+ if (statusBarManagerInternal != null) {
+ statusBarManagerInternal.setWindowState(
+ mDisplayContent.getDisplayId(), mId, state);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7daebff2ccc2..cbd1314b104a 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -307,11 +307,6 @@ class InsetsSourceProvider {
// to control the window for now.
return;
}
- if (target != null && target.getWindow() != null) {
- // ime control target could be a different window.
- // Refer WindowState#getImeControlTarget().
- target = target.getWindow().getImeControlTarget();
- }
if (mWin != null && mWin.getSurfaceControl() == null) {
// if window doesn't have a surface, set it null and return.
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3dbe79df6722..5a249a5599bb 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -58,10 +58,12 @@ public class Letterbox {
private final LetterboxSurface mLeft = new LetterboxSurface("left");
private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
private final LetterboxSurface mRight = new LetterboxSurface("right");
- // Prevents wallpaper from peeking through near rounded corners. It's not included in
- // mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains
- // or attachInput.
- private final LetterboxSurface mBehind = new LetterboxSurface("behind");
+ // One surface that fills the whole window is used over multiple surfaces to:
+ // - Prevents wallpaper from peeking through near rounded corners.
+ // - For "blurred wallpaper" background, to avoid having visible border between surfaces.
+ // One surface approach isn't always preferred over multiple surfaces due to rendering cost
+ // for overlaping an app window and letterbox surfaces.
+ private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
/**
@@ -104,7 +106,7 @@ public class Letterbox {
mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin);
mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin);
- mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin);
+ mFullWindowSurface.layout(outer.left, outer.top, outer.right, outer.bottom, surfaceOrigin);
}
@@ -168,37 +170,46 @@ public class Letterbox {
for (LetterboxSurface surface : mSurfaces) {
surface.remove();
}
- mBehind.remove();
+ mFullWindowSurface.remove();
}
/** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
public boolean needsApplySurfaceChanges() {
+ if (useFullWindowSurface()) {
+ return mFullWindowSurface.needsApplySurfaceChanges();
+ }
for (LetterboxSurface surface : mSurfaces) {
if (surface.needsApplySurfaceChanges()) {
return true;
}
}
- if (mAreCornersRounded.get() && mBehind.needsApplySurfaceChanges()) {
- return true;
- }
return false;
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- for (LetterboxSurface surface : mSurfaces) {
- surface.applySurfaceChanges(t);
- }
- if (mAreCornersRounded.get()) {
- mBehind.applySurfaceChanges(t);
+ if (useFullWindowSurface()) {
+ mFullWindowSurface.applySurfaceChanges(t);
+
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.remove();
+ }
} else {
- mBehind.remove();
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.applySurfaceChanges(t);
+ }
+
+ mFullWindowSurface.remove();
}
}
/** Enables touches to slide into other neighboring surfaces. */
void attachInput(WindowState win) {
- for (LetterboxSurface surface : mSurfaces) {
- surface.attachInput(win);
+ if (useFullWindowSurface()) {
+ mFullWindowSurface.attachInput(win);
+ } else {
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.attachInput(win);
+ }
}
}
@@ -208,6 +219,16 @@ public class Letterbox {
surface.mInputInterceptor.mWindowHandle.displayId = displayId;
}
}
+ if (mFullWindowSurface.mInputInterceptor != null) {
+ mFullWindowSurface.mInputInterceptor.mWindowHandle.displayId = displayId;
+ }
+ }
+
+ /**
+ * Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
+ */
+ private boolean useFullWindowSurface() {
+ return mAreCornersRounded.get() || mHasWallpaperBackgroundSupplier.get();
}
private static class InputInterceptor {
@@ -308,6 +329,10 @@ public class Letterbox {
mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
}
+ boolean isRemoved() {
+ return mSurface != null || mInputInterceptor != null;
+ }
+
public void remove() {
if (mSurface != null) {
mTransactionFactory.get().remove(mSurface).apply();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7174e68b06f4..eb7087cbc722 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.graphics.Color;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -105,12 +104,20 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
- @VisibleForTesting
void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
/**
+ * Resets the aspect ratio of letterbox for fixed orientation to {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
+ */
+ void resetFixedOrientationLetterboxAspectRatio() {
+ mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
+ }
+
+ /**
* Gets the aspect ratio of letterbox for fixed orientation.
*/
float getFixedOrientationLetterboxAspectRatio() {
@@ -118,6 +125,25 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+ * both it and a value of {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+ * and corners of the activity won't be rounded.
+ */
+ void setLetterboxActivityCornersRadius(int cornersRadius) {
+ mLetterboxActivityCornersRadius = cornersRadius;
+ }
+
+ /**
+ * Resets corners raidus for activities presented in the letterbox mode to {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+ */
+ void resetLetterboxActivityCornersRadius() {
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ }
+
+ /**
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
@@ -140,6 +166,25 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundColor;
}
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColor = color;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ }
+
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
* com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
@@ -149,6 +194,19 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundType;
}
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
/** Returns a string representing the given {@link LetterboxBackgroundType}. */
static String letterboxBackgroundTypeToString(
@LetterboxBackgroundType int backgroundType) {
@@ -178,6 +236,27 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides alpha of a black scrim shown over wallpaper for {@link
+ * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
+ *
+ * <p>If given value is < 0 or >= 1, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
+ * and 0.0 (transparent) is instead.
+ */
+ void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
+ }
+
+ /**
+ * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
+ */
+ void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+ }
+
+ /**
* Gets alpha of a black scrim shown over wallpaper letterbox background.
*/
float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
@@ -185,6 +264,28 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
+ * {@link mLetterboxBackgroundType}.
+ *
+ * <p> If given value <= 0, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
+ * and 0 is used instead.
+ */
+ void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
+ mLetterboxBackgroundWallpaperBlurRadius = radius;
+ }
+
+ /**
+ * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+ * mLetterboxBackgroundType} to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
+ */
+ void resetLetterboxBackgroundWallpaperBlurRadius() {
+ mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+ }
+
+ /**
* Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
* mLetterboxBackgroundType}.
*/
@@ -211,9 +312,17 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
* central position (0.5) is used.
*/
- @VisibleForTesting
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
mLetterboxHorizontalPositionMultiplier = multiplier;
}
+ /**
+ * Resets horizontal position of a center of the letterboxed app window to {@link
+ * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+ */
+ void resetLetterboxHorizontalPositionMultiplier() {
+ mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index a10b5d6e8177..7cd91646af1a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -25,6 +25,8 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -149,8 +151,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
// Invisible activity should be stopped. If the recents activity is alive and its doesn't
// need to relaunch by current configuration, then it may be already in stopped state.
- if (!targetActivity.isState(Task.ActivityState.STOPPING,
- Task.ActivityState.STOPPED)) {
+ if (!targetActivity.isState(STOPPING, STOPPED)) {
// Add to stopping instead of stop immediately. So the client has the chance to perform
// traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
// things (e.g. the measure can be done earlier). The actual stop will be performed when
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 32147215834f..bd00751c2f41 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,10 +37,10 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -53,6 +53,11 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -69,14 +74,9 @@ import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -508,6 +508,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mTaskSupervisor.updateTopResumedActivityIfNeeded();
}
+ @Override
+ boolean isAttached() {
+ return true;
+ }
+
/**
* Called when DisplayWindowSettings values may change.
*/
@@ -816,8 +821,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// Initialize state of exiting tokens.
- final int numDisplays = mChildren.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.setExitingTokensHasVisible(false);
}
@@ -866,7 +870,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
@@ -928,12 +932,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// Time to remove any exiting tokens?
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.removeExistingTokensIfPossible();
}
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
@@ -1864,7 +1868,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (focusedRootTask == null) {
return null;
}
- final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity != null && resumedActivity.app != null) {
return resumedActivity;
}
@@ -1886,11 +1890,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// foreground.
WindowProcessController fgApp = getItemFromRootTasks(rootTask -> {
if (isTopDisplayFocusedRootTask(rootTask)) {
- final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
if (resumedActivity != null) {
return resumedActivity.app;
- } else if (rootTask.getPausingActivity() != null) {
- return rootTask.getPausingActivity().app;
+ } else if (rootTask.getTopPausingActivity() != null) {
+ return rootTask.getTopPausingActivity().app;
}
}
return null;
@@ -1916,7 +1920,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return;
}
- if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
+ if (rootTask.getVisibility(null /* starting */)
+ == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
return;
}
@@ -1973,7 +1978,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- if (mTaskSupervisor.inActivityVisibilityUpdate()) {
+ if (mTaskSupervisor.inActivityVisibilityUpdate()
+ || mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
// Don't do recursive work.
return;
}
@@ -2185,7 +2191,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// display area, so reparent.
rootTask.reparent(taskDisplayArea, true /* onTop */);
}
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask);
+ mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
@@ -2386,7 +2392,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (displayShouldSleep) {
rootTask.goToSleepIfPossible(false /* shuttingDown */);
} else {
- rootTask.awakeFromSleepingLocked();
+ rootTask.forAllLeafTasksAndLeafTaskFragments(
+ taskFragment -> taskFragment.awakeFromSleeping(),
+ true /* traverseTopToBottom */);
if (rootTask.isFocusedRootTaskOnDisplay()
&& !mTaskSupervisor.getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2748,8 +2756,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (DEBUG_SWITCH) {
Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState()
- + " resumed=" + r.getTask().getResumedActivity() + " pausing="
- + r.getTask().getPausingActivity() + " for reason "
+ + " resumed=" + r.getTask().getTopResumedActivity() + " pausing="
+ + r.getTask().getTopPausingActivity() + " for reason "
+ mDestroyAllActivitiesReason);
}
@@ -3336,7 +3344,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (rootTask == null || !rootTask.hasActivity()) {
continue;
}
- final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
if (resumedActivity == null || !resumedActivity.idle) {
ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
+ "not idle", rootTask.getRootTaskId(), resumedActivity);
@@ -3351,7 +3359,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean allResumedActivitiesVisible() {
boolean[] foundResumed = {false};
final boolean foundInvisibleResumedActivity = forAllRootTasks(rootTask -> {
- final ActivityRecord r = rootTask.getResumedActivity();
+ final ActivityRecord r = rootTask.getTopResumedActivity();
if (r != null) {
if (!r.nowVisible) {
return true;
@@ -3369,7 +3377,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean allPausedActivitiesComplete() {
boolean[] pausing = {true};
final boolean hasActivityNotCompleted = forAllLeafTasks(task -> {
- final ActivityRecord r = task.getPausingActivity();
+ final ActivityRecord r = task.getTopPausingActivity();
if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: "
+ "r=%s state=%s", r, r.getState());
@@ -3544,6 +3552,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
+ // TODO(b/191434136): handle this properly when we add multi-window support on secondary
+ // display.
private void calculateDefaultMinimalSizeOfResizeableTasks() {
final Resources res = mService.mContext.getResources();
final float minimalSize = res.getDimension(
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 7ba772c18455..1533245a75ca 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -43,7 +43,11 @@ class RunningTasks {
// Comparator to sort by last active time (descending)
private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR =
- (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+ (o1, o2) -> {
+ return o1.lastActiveTime == o2.lastActiveTime
+ ? Integer.signum(o2.mTaskId - o1.mTaskId) :
+ Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+ };
private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 4892005631ba..2d4aef682d62 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -78,6 +78,18 @@ public class SafeActivityOptions {
}
/**
+ * Constructs a new instance from a bundle and provided pid/uid.
+ *
+ * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
+ */
+ static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) {
+ return bOptions != null
+ ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions),
+ callingPid, callingUid)
+ : null;
+ }
+
+ /**
* Constructs a new instance and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
* this object.
@@ -91,6 +103,17 @@ public class SafeActivityOptions {
}
/**
+ * Constructs a new instance.
+ *
+ * @param options The options to wrap.
+ */
+ private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
+ mOriginalCallingPid = callingPid;
+ mOriginalCallingUid = callingUid;
+ mOriginalOptions = options;
+ }
+
+ /**
* Overrides options with options from a caller and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
* method.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 936b2efa00ad..25bdea6576a5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,13 +27,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
@@ -43,7 +40,6 @@ import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
@@ -53,9 +49,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -67,7 +60,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -85,21 +77,18 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMA
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.TRANSFER_SPLASH_SCREEN_COPYING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
@@ -112,7 +101,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
@@ -123,21 +111,12 @@ import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
import static com.android.server.wm.TaskProto.AFFINITY;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
-import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MIN_HEIGHT;
-import static com.android.server.wm.TaskProto.MIN_WIDTH;
import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
import static com.android.server.wm.TaskProto.RESIZE_MODE;
@@ -145,7 +124,7 @@ import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
+import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
@@ -170,14 +149,7 @@ import android.app.AppGlobals;
import android.app.IActivityController;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
-import android.app.ResultInfo;
import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -246,23 +218,21 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-class Task extends WindowContainer<WindowContainer> {
+/**
+ * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.
+ * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}
+ * can also be an entity that showing in the Recents Screen for a job that user interacted with.
+ * A {@link Task} can also contain other {@link Task}s.
+ */
+class Task extends TaskFragment {
private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
- static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
- private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
- private static final String TAG_APP = TAG + POSTFIX_APP;
static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
- private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
- private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
- private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
- private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
@@ -305,10 +275,6 @@ class Task extends WindowContainer<WindowContainer> {
private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets";
private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size";
- // Set to false to disable the preview that is shown while a new activity
- // is being started.
- private static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
// How long to wait for all background Activities to redraw following a call to
// convertToTranslucent().
private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
@@ -317,7 +283,6 @@ class Task extends WindowContainer<WindowContainer> {
// code.
static final int PERSIST_TASK_VERSION = 1;
- static final int INVALID_MIN_SIZE = -1;
private float mShadowRadius = 0;
/**
@@ -337,36 +302,6 @@ class Task extends WindowContainer<WindowContainer> {
// Do not move the root task as a part of reparenting
static final int REPARENT_LEAVE_ROOT_TASK_IN_PLACE = 2;
- @IntDef(prefix = {"TASK_VISIBILITY"}, value = {
- TASK_VISIBILITY_VISIBLE,
- TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- TASK_VISIBILITY_INVISIBLE,
- })
- @interface TaskVisibility {}
-
- /** Task is visible. No other tasks on top that fully or partially occlude it. */
- static final int TASK_VISIBILITY_VISIBLE = 0;
-
- /** Task is partially occluded by other translucent task(s) on top of it. */
- static final int TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
-
- /** Task is completely invisible. */
- static final int TASK_VISIBILITY_INVISIBLE = 2;
-
- enum ActivityState {
- INITIALIZING,
- STARTED,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED,
- RESTARTING_PROCESS
- }
-
// The topmost Activity passed to convertToTranslucent(). When non-null it means we are
// waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
// are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
@@ -448,7 +383,6 @@ class Task extends WindowContainer<WindowContainer> {
CharSequence lastDescription; // Last description captured for this item.
- Task mAdjacentTask; // Task adjacent to this one.
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
Task mPrevAffiliate; // previous task in affiliated chain.
int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
@@ -460,21 +394,12 @@ class Task extends WindowContainer<WindowContainer> {
String mCallingPackage;
String mCallingFeatureId;
- private final Rect mTmpStableBounds = new Rect();
- private final Rect mTmpNonDecorBounds = new Rect();
- private final Rect mTmpBounds = new Rect();
- private final Rect mTmpInsets = new Rect();
- private final Rect mTmpFullBounds = new Rect();
private static final Rect sTmpBounds = new Rect();
// Last non-fullscreen bounds the task was launched in or resized to.
// The information is persisted and used to determine the appropriate root task to launch the
// task into on restore.
Rect mLastNonFullscreenBounds = null;
- // Minimal width and height of this task when it's resizeable. -1 means it should use the
- // default minimal width/height.
- int mMinWidth;
- int mMinHeight;
// The surface transition of the target when recents animation is finished.
// This is originally introduced to carry out the current surface control position and window
@@ -499,10 +424,6 @@ class Task extends WindowContainer<WindowContainer> {
/** Used by fillTaskInfo */
final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport();
- final ActivityTaskManagerService mAtmService;
- final ActivityTaskSupervisor mTaskSupervisor;
- final RootWindowContainer mRootWindowContainer;
-
/* Unique identifier for this task. */
final int mTaskId;
/* User for which this task was created. */
@@ -573,29 +494,6 @@ class Task extends WindowContainer<WindowContainer> {
/** ActivityRecords that are exiting, but still on screen for animations. */
final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
- /**
- * When we are in the process of pausing an activity, before starting the
- * next one, this variable holds the activity that is currently being paused.
- *
- * Only set at leaf tasks.
- */
- @Nullable
- private ActivityRecord mPausingActivity = null;
-
- /**
- * This is the last activity that we put into the paused state. This is
- * used to determine if we need to do an activity transition while sleeping,
- * when we normally hold the top activity paused.
- */
- ActivityRecord mLastPausedActivity = null;
-
- /**
- * Current activity that is resumed, or null if there is none.
- * Only set at leaf tasks.
- */
- @Nullable
- private ActivityRecord mResumedActivity = null;
-
private boolean mForceShowForAllUsers;
/** When set, will force the task to report as invisible. */
@@ -643,121 +541,6 @@ class Task extends WindowContainer<WindowContainer> {
}
private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
- private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
- new EnsureActivitiesVisibleHelper(this);
- private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
- new EnsureVisibleActivitiesConfigHelper();
- private class EnsureVisibleActivitiesConfigHelper {
- private boolean mUpdateConfig;
- private boolean mPreserveWindow;
- private boolean mBehindFullscreen;
-
- void reset(boolean preserveWindow) {
- mPreserveWindow = preserveWindow;
- mUpdateConfig = false;
- mBehindFullscreen = false;
- }
-
- void process(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
- return;
- }
- reset(preserveWindow);
-
- final PooledFunction f = PooledLambda.obtainFunction(
- EnsureVisibleActivitiesConfigHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
- f.recycle();
-
- if (mUpdateConfig) {
- // Ensure the resumed state of the focus activity if we updated the configuration of
- // any activity.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- }
-
- boolean processActivity(ActivityRecord r) {
- mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
- mBehindFullscreen |= r.occludesParent();
- return mBehindFullscreen;
- }
- }
-
- private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
- new CheckBehindFullscreenActivityHelper();
- private class CheckBehindFullscreenActivityHelper {
- private boolean mAboveTop;
- private boolean mBehindFullscreenActivity;
- private ActivityRecord mToCheck;
- private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
- private boolean mHandlingOccluded;
-
- private void reset(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- mToCheck = toCheck;
- mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
- mAboveTop = true;
- mBehindFullscreenActivity = false;
-
- if (!shouldBeVisible(null)) {
- // The root task is not visible, so no activity in it should be displaying a
- // starting window. Mark all activities below top and behind fullscreen.
- mAboveTop = false;
- mBehindFullscreenActivity = true;
- }
-
- mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
- }
-
- boolean process(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- reset(toCheck, handleBehindFullscreenActivity);
-
- if (!mHandlingOccluded && mBehindFullscreenActivity) {
- return true;
- }
-
- final ActivityRecord topActivity = topRunningActivity();
- final PooledFunction f = PooledLambda.obtainFunction(
- CheckBehindFullscreenActivityHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class), topActivity);
- forAllActivities(f);
- f.recycle();
-
- return mBehindFullscreenActivity;
- }
-
- /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
- private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
- if (mAboveTop) {
- if (r == topActivity) {
- if (r == mToCheck) {
- // It is the top activity in a visible root task.
- mBehindFullscreenActivity = false;
- return true;
- }
- mAboveTop = false;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
-
- if (mHandlingOccluded) {
- // Iterating through all occluded activities.
- if (mBehindFullscreenActivity) {
- mHandleBehindFullscreenActivity.accept(r);
- }
- } else if (r == mToCheck) {
- return true;
- } else if (mBehindFullscreenActivity) {
- // It is occluded before {@param toCheck} is found.
- return true;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
- }
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
@@ -821,18 +604,6 @@ class Task extends WindowContainer<WindowContainer> {
*/
private boolean mForceNotOrganized;
- /**
- * This task was created by the task organizer which has the following implementations.
- * <ul>
- * <lis>The task won't be removed when it is empty. Removal has to be an explicit request
- * from the task organizer.</li>
- * <li>Unlike other non-root tasks, it's direct children are visible to the task
- * organizer for ordering purposes.</li>
- * </ul>
- */
- @VisibleForTesting
- boolean mCreatedByOrganizer;
-
// Tracking cookie for the creation of this task.
IBinder mLaunchCookie;
@@ -860,11 +631,8 @@ class Task extends WindowContainer<WindowContainer> {
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
boolean _removeWithTaskOrganizer) {
- super(atmService.mWindowManager);
+ super(atmService, null /* fragmentToken */, _createdByOrganizer);
- mAtmService = atmService;
- mTaskSupervisor = atmService.mTaskSupervisor;
- mRootWindowContainer = mAtmService.mRootWindowContainer;
mTaskId = _taskId;
mUserId = _userId;
mResizeMode = resizeMode;
@@ -877,7 +645,6 @@ class Task extends WindowContainer<WindowContainer> {
: new PersistedTaskSnapshotData();
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
- mRemoteToken = new RemoteToken(this);
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -915,7 +682,6 @@ class Task extends WindowContainer<WindowContainer> {
mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
- mCreatedByOrganizer = _createdByOrganizer;
mLaunchCookie = _launchCookie;
mDeferTaskAppear = _deferTaskAppear;
mRemoveWithTaskOrganizer = _removeWithTaskOrganizer;
@@ -1444,6 +1210,12 @@ class Task extends WindowContainer<WindowContainer> {
}
if (newParent != null) {
+ // Surface of Task that will not be organized should be shown by default.
+ // See Task#showSurfaceOnCreation
+ if (!mCreatedByOrganizer && !canBeOrganized()) {
+ getSyncTransaction().show(mSurfaceControl);
+ }
+
// TODO: Ensure that this is actually necessary here
// Notify the voice session if required
if (voiceSession != null) {
@@ -1468,64 +1240,80 @@ class Task extends WindowContainer<WindowContainer> {
mRootWindowContainer.updateUIDsPresentOnDisplay();
}
- void cleanUpActivityReferences(ActivityRecord r) {
- // mPausingActivity is set at leaf task
- if (mPausingActivity != null && mPausingActivity == r) {
- mPausingActivity = null;
- }
-
- if (mResumedActivity != null && mResumedActivity == r) {
- setResumedActivity(null, "cleanUpActivityReferences");
- }
-
- final WindowContainer parent = getParent();
- if (parent != null && parent.asTask() != null) {
- parent.asTask().cleanUpActivityReferences(r);
- return;
+ /** Returns the currently topmost resumed activity. */
+ @Nullable
+ ActivityRecord getTopResumedActivity() {
+ if (!isLeafTask()) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ ActivityRecord resumedActivity = mChildren.get(i).asTask().getTopResumedActivity();
+ if (resumedActivity != null) {
+ return resumedActivity;
+ }
+ }
}
- r.removeTimeouts();
- mExitingActivities.remove(r);
- }
- /** @return the currently resumed activity. */
- ActivityRecord getResumedActivity() {
- if (isLeafTask()) {
- return mResumedActivity;
+ final ActivityRecord taskResumedActivity = getResumedActivity();
+ ActivityRecord topResumedActivity = null;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ final ActivityRecord[] resumedActivity = new ActivityRecord[1];
+ child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
+ if (fragment.getResumedActivity() != null) {
+ resumedActivity[0] = fragment.getResumedActivity();
+ return true;
+ }
+ return false;
+ });
+ topResumedActivity = resumedActivity[0];
+ } else if (taskResumedActivity != null
+ && child.asActivityRecord() == taskResumedActivity) {
+ topResumedActivity = taskResumedActivity;
+ }
+ if (topResumedActivity != null) {
+ return topResumedActivity;
+ }
}
-
- final Task task = getTask(t -> t.mResumedActivity != null, true /* traverseTopToBottom */);
- return task != null ? task.mResumedActivity : null;
- }
-
- @VisibleForTesting
- void setPausingActivity(ActivityRecord pausing) {
- mPausingActivity = pausing;
+ return null;
}
/**
- * @return the currently pausing activity of this task or the topmost pausing activity of the
- * child tasks
+ * Returns the currently topmost pausing activity.
*/
- ActivityRecord getPausingActivity() {
- if (isLeafTask()) {
- return mPausingActivity;
+ @Nullable
+ ActivityRecord getTopPausingActivity() {
+ if (!isLeafTask()) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ ActivityRecord pausingActivity = mChildren.get(i).asTask().getTopPausingActivity();
+ if (pausingActivity != null) {
+ return pausingActivity;
+ }
+ }
}
- final Task task = getTask(t -> t.mPausingActivity != null, true /* traverseTopToBottom */);
- return task != null ? task.mPausingActivity : null;
- }
-
- void setResumedActivity(ActivityRecord r, String reason) {
- warnForNonLeafTask("setResumedActivity");
- if (mResumedActivity == r) {
- return;
+ final ActivityRecord taskPausingActivity = getPausingActivity();
+ ActivityRecord topPausingActivity = null;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ final ActivityRecord[] pausingActivity = new ActivityRecord[1];
+ child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
+ if (fragment.getPausingActivity() != null) {
+ pausingActivity[0] = fragment.getPausingActivity();
+ return true;
+ }
+ return false;
+ });
+ topPausingActivity = pausingActivity[0];
+ } else if (taskPausingActivity != null
+ && child.asActivityRecord() == taskPausingActivity) {
+ topPausingActivity = taskPausingActivity;
+ }
+ if (topPausingActivity != null) {
+ return topPausingActivity;
+ }
}
-
- if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
- "setResumedActivity task:" + this + " + from: "
- + mResumedActivity + " to:" + r + " reason:" + reason);
- mResumedActivity = r;
- mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ return null;
}
void updateTaskMovement(boolean toTop, int position) {
@@ -1564,11 +1352,6 @@ class Task extends WindowContainer<WindowContainer> {
mTaskId, mUserId);
}
- void setAdjacentTask(Task adjacent) {
- mAdjacentTask = adjacent;
- adjacent.mAdjacentTask = this;
- }
-
void setTaskToAffiliateWith(Task taskToAffiliateWith) {
closeRecentsChain();
mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -1677,22 +1460,7 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
- public int getActivityType() {
- final int applicationType = super.getActivityType();
- if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
- return applicationType;
- }
- return getTopChild().getActivityType();
- }
-
- @Override
void addChild(WindowContainer child, int index) {
- // If this task had any child before we added this one.
- boolean hadChild = hasChild();
- // getActivityType() looks at the top child, so we need to read the type before adding
- // a new child in case the new child is on top and UNDEFINED.
- final int activityType = getActivityType();
-
index = getAdjustedChildPosition(child, index);
super.addChild(child, index);
@@ -1708,10 +1476,17 @@ class Task extends WindowContainer<WindowContainer> {
// now that this record is in a new task.
mRootWindowContainer.updateUIDsPresentOnDisplay();
- final ActivityRecord r = child.asActivityRecord();
- if (r == null) return;
+ // Only pass minimum dimensions for pure TaskFragment. Task's minimum dimensions must be
+ // passed from Task constructor.
+ final TaskFragment childTaskFrag = child.asTaskFragment();
+ if (childTaskFrag != null && childTaskFrag.asTask() == null) {
+ childTaskFrag.setMinDimensions(mMinWidth, mMinHeight);
+ }
+ }
- r.inHistory = true;
+ /** Called when an {@link ActivityRecord} is added as a descendant */
+ void onDescendantActivityAdded(boolean hadChild, int activityType, ActivityRecord r) {
+ warnForNonLeafTask("onDescendantActivityAdded");
// Only set this based on the first activity
if (!hadChild) {
@@ -1738,10 +1513,6 @@ class Task extends WindowContainer<WindowContainer> {
updateEffectiveIntent();
}
- void addChild(ActivityRecord r) {
- addChild(r, Integer.MAX_VALUE /* add on top */);
- }
-
@Override
void removeChild(WindowContainer child) {
removeChild(child, "removeChild");
@@ -1987,32 +1758,6 @@ class Task extends WindowContainer<WindowContainer> {
&& supportsMultiWindowInDisplayArea(tda);
}
- boolean supportsMultiWindow() {
- return supportsMultiWindowInDisplayArea(getDisplayArea());
- }
-
- /**
- * @return whether this task supports multi-window if it is in the given
- * {@link TaskDisplayArea}.
- */
- boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
- if (!mAtmService.mSupportsMultiWindow) {
- return false;
- }
- if (tda == null) {
- Slog.w(TAG_TASKS, "Can't find TaskDisplayArea to determine support for multi"
- + " window. Task id=" + mTaskId + " attached=" + isAttached());
- return false;
- }
-
- if (!isResizeable() && !tda.supportsNonResizableMultiWindow()) {
- // Not support non-resizable in multi window.
- return false;
- }
-
- return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
- }
-
/**
* Check whether this task can be launched on the specified display.
*
@@ -2148,60 +1893,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
- @NonNull Configuration parentConfig) {
- int minWidth = mMinWidth;
- int minHeight = mMinHeight;
- // If the task has no requested minimal size, we'd like to enforce a minimal size
- // so that the user can not render the task too small to manipulate. We don't need
- // to do this for the root pinned task as the bounds are controlled by the system.
- if (!inPinnedWindowingMode()) {
- final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
- final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- final int defaultMinSize = (int) (defaultMinSizeDp * density);
-
- if (minWidth == INVALID_MIN_SIZE) {
- minWidth = defaultMinSize;
- }
- if (minHeight == INVALID_MIN_SIZE) {
- minHeight = defaultMinSize;
- }
- }
- if (bounds.isEmpty()) {
- // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
- // do, we can just skip.
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
- return;
- }
- bounds.set(parentBounds);
- }
- final boolean adjustWidth = minWidth > bounds.width();
- final boolean adjustHeight = minHeight > bounds.height();
- if (!(adjustWidth || adjustHeight)) {
- return;
- }
-
- if (adjustWidth) {
- if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
- bounds.left = bounds.right - minWidth;
- } else {
- // Either left bounds match, or neither match, or the previous bounds were
- // fullscreen and we default to keeping left.
- bounds.right = bounds.left + minWidth;
- }
- }
- if (adjustHeight) {
- if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
- bounds.top = bounds.bottom - minHeight;
- } else {
- // Either top bounds match, or neither match, or the previous bounds were
- // fullscreen and we default to keeping top.
- bounds.bottom = bounds.top + minHeight;
- }
- }
- }
-
void setLastNonFullscreenBounds(Rect bounds) {
if (mLastNonFullscreenBounds == null) {
mLastNonFullscreenBounds = new Rect(bounds);
@@ -2210,32 +1901,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /**
- * This should be called when an child activity changes state. This should only
- * be called from
- * {@link ActivityRecord#setState(ActivityState, String)} .
- * @param record The {@link ActivityRecord} whose state has changed.
- * @param state The new state.
- * @param reason The reason for the change.
- */
- void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
- warnForNonLeafTask("onActivityStateChanged");
- if (record == mResumedActivity && state != RESUMED) {
- setResumedActivity(null, reason + " - onActivityStateChanged");
- }
-
- if (state == RESUMED) {
- if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
- Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason);
- }
- setResumedActivity(record, reason + " - onActivityStateChanged");
- if (record == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(record, reason);
- }
- mTaskSupervisor.mRecentTasks.add(record.getTask());
- }
- }
-
private void onConfigurationChangedInner(Configuration newParentConfig) {
// Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
// restore the last recorded non-fullscreen bounds.
@@ -2528,400 +2193,6 @@ class Task extends WindowContainer<WindowContainer> {
mTaskSupervisor.mLaunchParamsPersister.saveTask(this, display);
}
- /**
- * Adjust bounds to stay within root task bounds.
- *
- * Since bounds might be outside of root task bounds, this method tries to move the bounds in
- * a way that keep them unchanged, but be contained within the root task bounds.
- *
- * @param bounds Bounds to be adjusted.
- * @param rootTaskBounds Bounds within which the other bounds should remain.
- * @param overlapPxX The amount of px required to be visible in the X dimension.
- * @param overlapPxY The amount of px required to be visible in the Y dimension.
- */
- private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
- int overlapPxY) {
- if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
- return;
- }
-
- // For each side of the parent (eg. left), check if the opposing side of the window (eg.
- // right) is at least overlap pixels away. If less, offset the window by that difference.
- int horizontalDiff = 0;
- // If window is smaller than overlap, use it's smallest dimension instead
- int overlapLR = Math.min(overlapPxX, bounds.width());
- if (bounds.right < (rootTaskBounds.left + overlapLR)) {
- horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
- } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
- horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
- }
- int verticalDiff = 0;
- int overlapTB = Math.min(overlapPxY, bounds.width());
- if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
- verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
- } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
- verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
- }
- bounds.offset(horizontalDiff, verticalDiff);
- }
-
- /**
- * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
- * intersectBounds on a side, then the respective side will not be intersected.
- *
- * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
- * inset on that side is no-longer applicable. This scenario happens when a task's minimal
- * bounds are larger than the provided parent/display bounds.
- *
- * @param inOutBounds the bounds to intersect.
- * @param intersectBounds the bounds to intersect with.
- * @param intersectInsets insets to apply to intersectBounds before intersecting.
- */
- static void intersectWithInsetsIfFits(
- Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
- if (inOutBounds.right <= intersectBounds.right) {
- inOutBounds.right =
- Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
- }
- if (inOutBounds.bottom <= intersectBounds.bottom) {
- inOutBounds.bottom =
- Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
- }
- if (inOutBounds.left >= intersectBounds.left) {
- inOutBounds.left =
- Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
- }
- if (inOutBounds.top >= intersectBounds.top) {
- inOutBounds.top =
- Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
- }
- }
-
- /**
- * Gets bounds with non-decor and stable insets applied respectively.
- *
- * If bounds overhangs the display, those edges will not get insets. See
- * {@link #intersectWithInsetsIfFits}
- *
- * @param outNonDecorBounds where to place bounds with non-decor insets applied.
- * @param outStableBounds where to place bounds with stable insets applied.
- * @param bounds the bounds to inset.
- */
- private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
- DisplayInfo displayInfo) {
- outNonDecorBounds.set(bounds);
- outStableBounds.set(bounds);
- final Task rootTask = getRootTask();
- if (rootTask == null || rootTask.mDisplayContent == null) {
- return;
- }
- mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-
- final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
- intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
-
- policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
- intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
- }
-
- /**
- * Forces the app bounds related configuration can be computed by
- * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
- * ActivityRecord.CompatDisplayInsets)}.
- */
- private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
- final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (appBounds != null) {
- appBounds.setEmpty();
- }
- inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
- if (overrideDisplayInfo != null) {
- // Make sure the screen related configs can be computed by the provided display info.
- inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig) {
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- if (compatInsets != null) {
- // Make sure the app bounds can be computed by the compat insets.
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- compatInsets);
- }
-
- /**
- * Calculates configuration values used by the client to get resources. This should be run
- * using app-facing bounds (bounds unmodified by animations or transient interactions).
- *
- * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
- * configuring an "inherit-bounds" window which means that all configuration settings would
- * just be inherited from the parent configuration.
- **/
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = parentConfig.windowConfiguration.getWindowingMode();
- }
-
- float density = inOutConfig.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = parentConfig.densityDpi;
- }
- density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-
- // The bounds may have been overridden at this level. If the parent cannot cover these
- // bounds, the configuration is still computed according to the override bounds.
- final boolean insideParentBounds;
-
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
- if (resolvedBounds == null || resolvedBounds.isEmpty()) {
- mTmpFullBounds.set(parentBounds);
- insideParentBounds = true;
- } else {
- mTmpFullBounds.set(resolvedBounds);
- insideParentBounds = parentBounds.contains(resolvedBounds);
- }
-
- // Non-null compatibility insets means the activity prefers to keep its original size, so
- // out bounds doesn't need to be restricted by the parent or current display
- final boolean customContainerPolicy = compatInsets != null;
-
- Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (outAppBounds == null || outAppBounds.isEmpty()) {
- // App-bounds hasn't been overridden, so calculate a value for it.
- inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
- outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-
- if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
- final Rect containingAppBounds;
- if (insideParentBounds) {
- containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
- } else {
- // Restrict appBounds to display non-decor rather than parent because the
- // override bounds are beyond the parent. Otherwise, it won't match the
- // overridden bounds.
- final TaskDisplayArea displayArea = getDisplayArea();
- containingAppBounds = displayArea != null
- ? displayArea.getWindowConfiguration().getAppBounds() : null;
- }
- if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
- outAppBounds.intersect(containingAppBounds);
- }
- }
- }
-
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
- || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
- mTmpNonDecorBounds.set(mTmpFullBounds);
- mTmpStableBounds.set(mTmpFullBounds);
- } else if (!customContainerPolicy
- && (overrideDisplayInfo != null || getDisplayContent() != null)) {
- final DisplayInfo di = overrideDisplayInfo != null
- ? overrideDisplayInfo
- : getDisplayContent().getDisplayInfo();
-
- // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
- // area, i.e. the screen area without the system bars.
- // The non decor inset are areas that could never be removed in Honeycomb. See
- // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
- } else {
- // Apply the given non-decor and stable insets to calculate the corresponding bounds
- // for screen size of configuration.
- int rotation = inOutConfig.windowConfiguration.getRotation();
- if (rotation == ROTATION_UNDEFINED) {
- rotation = parentConfig.windowConfiguration.getRotation();
- }
- if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
- mTmpNonDecorBounds.set(mTmpFullBounds);
- mTmpStableBounds.set(mTmpFullBounds);
- compatInsets.getBoundsByRotation(mTmpBounds, rotation);
- intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
- compatInsets.mNonDecorInsets[rotation]);
- intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
- compatInsets.mStableInsets[rotation]);
- outAppBounds.set(mTmpNonDecorBounds);
- } else {
- // Set to app bounds because it excludes decor insets.
- mTmpNonDecorBounds.set(outAppBounds);
- mTmpStableBounds.set(outAppBounds);
- }
- }
-
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
- inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
- ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
- : overrideScreenWidthDp;
- }
- if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
- inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
- ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
- : overrideScreenHeightDp;
- }
-
- if (inOutConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- if (WindowConfiguration.isFloating(windowingMode)) {
- // For floating tasks, calculate the smallest width from the bounds of the task
- inOutConfig.smallestScreenWidthDp = (int) (
- Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
- }
- // otherwise, it will just inherit
- }
- }
-
- if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
- inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
- }
- if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
- // For calculating screen layout, we need to use the non-decor inset screen area for the
- // calculation for compatibility reasons, i.e. screen area without system bars that
- // could never go away in Honeycomb.
- int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
- // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
- // undefined so it can't be used.
- if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- compatScreenWidthDp = inOutConfig.screenWidthDp;
- }
- if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- compatScreenHeightDp = inOutConfig.screenHeightDp;
- }
- // Reducing the screen layout starting from its parent config.
- inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
- compatScreenWidthDp, compatScreenHeightDp);
- }
- }
-
- /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
- static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
- int screenHeightDp) {
- sourceScreenLayout = sourceScreenLayout
- & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
- final int longSize = Math.max(screenWidthDp, screenHeightDp);
- final int shortSize = Math.min(screenWidthDp, screenHeightDp);
- return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
- }
-
- @Override
- void resolveOverrideConfiguration(Configuration newParentConfig) {
- mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- super.resolveOverrideConfiguration(newParentConfig);
-
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
- final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
-
- // Resolve override windowing mode to fullscreen for home task (even on freeform
- // display), or split-screen if in split-screen mode.
- if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
- ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- }
-
- // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
- // pinned windowing mode.
- if (!supportsMultiWindow()) {
- final int candidateWindowingMode =
- windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
- if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
- && candidateWindowingMode != WINDOWING_MODE_PINNED) {
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
- WINDOWING_MODE_FULLSCREEN);
- }
- }
-
- if (isLeafTask()) {
- resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
- }
- computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
- }
-
- private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
- Rect previousBounds) {
-
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
- }
- // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
- // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
- getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- Rect outOverrideBounds =
- getResolvedOverrideConfiguration().windowConfiguration.getBounds();
-
- if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
- // Use empty bounds to indicate "fill parent".
- outOverrideBounds.setEmpty();
- // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
- // the parent or display is smaller than the size, the content may be cropped.
- return;
- }
-
- adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- computeFreeformBounds(outOverrideBounds, newParentConfig);
- return;
- }
- }
-
- /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
- private void computeFreeformBounds(@NonNull Rect outBounds,
- @NonNull Configuration newParentConfig) {
- // by policy, make sure the window remains within parent somewhere
- final float density =
- ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
- final Rect parentBounds =
- new Rect(newParentConfig.windowConfiguration.getBounds());
- final DisplayContent display = getDisplayContent();
- if (display != null) {
- // If a freeform window moves below system bar, there is no way to move it again
- // by touch. Because its caption is covered by system bar. So we exclude them
- // from root task bounds. and then caption will be shown inside stable area.
- final Rect stableBounds = new Rect();
- display.getStableRect(stableBounds);
- parentBounds.intersect(stableBounds);
- }
-
- fitWithinBounds(outBounds, parentBounds,
- (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
- (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
-
- // Prevent to overlap caption with stable insets.
- final int offsetTop = parentBounds.top - outBounds.top;
- if (offsetTop > 0) {
- outBounds.offset(0, offsetTop);
- }
- }
-
Rect updateOverrideConfigurationFromLaunchBounds() {
// If the task is controlled by another organized task, do not set override
// configurations and let its parent (organized task) to control it;
@@ -2970,22 +2241,14 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- int getDisplayId() {
- final DisplayContent dc = getDisplayContent();
- return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
- }
-
/** @return Id of root task. */
int getRootTaskId() {
return getRootTask().mTaskId;
}
+ @Nullable
Task getRootTask() {
- final WindowContainer parent = getParent();
- if (parent == null) return this;
-
- final Task parentTask = parent.asTask();
- return parentTask == null ? this : parentTask.getRootTask();
+ return getRootTaskFragment().asTask();
}
/** @return the first organized task. */
@@ -3100,12 +2363,12 @@ class Task extends WindowContainer<WindowContainer> {
// and focused application if needed.
focusableTask.moveToFront(myReason);
// Top display focused root task is changed, update top resumed activity if needed.
- if (rootTask.getResumedActivity() != null) {
+ if (rootTask.getTopResumedActivity() != null) {
mTaskSupervisor.updateTopResumedActivityIfNeeded();
// Set focused app directly because if the next focused activity is already resumed
// (e.g. the next top activity is on a different display), there won't have activity
// state change to update it.
- mAtmService.setResumedActivityUncheckLocked(rootTask.getResumedActivity(), reason);
+ mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason);
}
return rootTask;
}
@@ -3154,17 +2417,16 @@ class Task extends WindowContainer<WindowContainer> {
// Figure-out min/max possible position depending on if child can show for current user.
int minPosition = (canShowChild) ? computeMinUserPosition(0, size) : 0;
- int maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
- if (!hasChild(wc)) {
- // Increase the maxPosition because children size will grow once wc is added.
- ++maxPosition;
+ int maxPosition = minPosition;
+ if (size > 0) {
+ maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
}
// Factor in always-on-top children in max possible position.
if (!wc.isAlwaysOnTop()) {
// We want to place all non-always-on-top containers below always-on-top ones.
while (maxPosition > minPosition) {
- if (!mChildren.get(maxPosition - 1).isAlwaysOnTop()) break;
+ if (!mChildren.get(maxPosition).isAlwaysOnTop()) break;
--maxPosition;
}
}
@@ -3175,6 +2437,12 @@ class Task extends WindowContainer<WindowContainer> {
} else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
return POSITION_TOP;
}
+
+ // Increase the maxPosition because children size will grow once wc is added.
+ if (!hasChild(wc)) {
+ ++maxPosition;
+ }
+
// Reset position based on minimum/maximum possible positions.
return Math.min(Math.max(suggestedPosition, minPosition), maxPosition);
}
@@ -3556,18 +2824,6 @@ class Task extends WindowContainer<WindowContainer> {
mForceShowForAllUsers = forceShowForAllUsers;
}
- @Override
- public boolean isAttached() {
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- return taskDisplayArea != null && !taskDisplayArea.isRemoved();
- }
-
- @Override
- @Nullable
- TaskDisplayArea getDisplayArea() {
- return (TaskDisplayArea) super.getDisplayArea();
- }
-
/**
* When we are in a floating root task (Freeform, Pinned, ...) we calculate
* insets differently. However if we are animating to the fullscreen root task
@@ -3578,45 +2834,6 @@ class Task extends WindowContainer<WindowContainer> {
return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
}
- /**
- * Returns true if the root task is translucent and can have other contents visible behind it if
- * needed. A root task is considered translucent if it don't contain a visible or
- * starting (about to be visible) activity that is fullscreen (opaque).
- * @param starting The currently starting activity or null if there is none.
- */
- @VisibleForTesting
- boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
- return true;
- }
- final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
- PooledLambda.__(ActivityRecord.class), starting);
- final ActivityRecord opaque = getActivity(p);
- p.recycle();
- return opaque == null;
- }
-
- private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
- if (r.finishing) {
- // We don't factor in finishing activities when determining translucency since
- // they will be gone soon.
- return false;
- }
-
- if (!r.visibleIgnoringKeyguard && r != starting) {
- // Also ignore invisible activities that are not the currently starting
- // activity (about to be visible).
- return false;
- }
-
- if (r.occludesParent()) {
- // Root task isn't translucent if it has at least one fullscreen activity
- // that is visible.
- return true;
- }
- return false;
- }
-
/** Returns the top-most activity that occludes the given one, or {@code null} if none. */
@Nullable
ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
@@ -3710,19 +2927,6 @@ class Task extends WindowContainer<WindowContainer> {
return activity != null ? activity.findMainWindow() : null;
}
- ActivityRecord topRunningActivity() {
- return topRunningActivity(false /* focusableOnly */);
- }
-
- ActivityRecord topRunningActivity(boolean focusableOnly) {
- // Split into 2 to avoid object creation due to variable capture.
- if (focusableOnly) {
- return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
- } else {
- return getActivity(ActivityRecord::canBeTopRunning);
- }
- }
-
ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTopRunningNonDelayed
, PooledLambda.__(ActivityRecord.class), notTop);
@@ -3777,12 +2981,6 @@ class Task extends WindowContainer<WindowContainer> {
});
}
- boolean isTopActivityFocusable() {
- final ActivityRecord r = topRunningActivity();
- return r != null ? r.isFocusable()
- : (isFocusable() && getWindowConfiguration().canReceiveKeys());
- }
-
boolean isFocusableAndVisible() {
return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
}
@@ -3898,6 +3096,41 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
+ /** Iterates through all leaf task fragments and the leaf tasks. */
+ void forAllLeafTasksAndLeafTaskFragments(final Consumer<TaskFragment> callback,
+ boolean traverseTopToBottom) {
+ forAllLeafTasks(task -> {
+ if (task.isLeafTaskFragment()) {
+ callback.accept(task);
+ return;
+ }
+
+ // A leaf task that may contains both activities and task fragments.
+ boolean consumed = false;
+ if (traverseTopToBottom) {
+ for (int i = task.mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ } else if (child.asActivityRecord() != null && !consumed) {
+ callback.accept(task);
+ consumed = true;
+ }
+ }
+ } else {
+ for (int i = 0; i < task.mChildren.size(); i++) {
+ final WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ } else if (child.asActivityRecord() != null && !consumed) {
+ callback.accept(task);
+ consumed = true;
+ }
+ }
+ }
+ }, traverseTopToBottom);
+ }
+
@Override
boolean forAllRootTasks(Function<Task, Boolean> callback, boolean traverseTopToBottom) {
return isRootTask() ? callback.apply(this) : false;
@@ -4068,6 +3301,9 @@ class Task extends WindowContainer<WindowContainer> {
info.userId = isLeafTask() ? mUserId : mCurrentUser;
info.taskId = mTaskId;
info.displayId = getDisplayId();
+ if (tda != null) {
+ info.displayAreaFeatureId = tda.mFeatureId;
+ }
info.isRunning = getTopNonFinishingActivity() != null;
final Intent baseIntent = getBaseIntent();
// Make a copy of base intent because this is like a snapshot info.
@@ -4127,6 +3363,7 @@ class Task extends WindowContainer<WindowContainer> {
: INVALID_TASK_ID;
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
+ info.isSleeping = shouldSleepActivities();
ActivityRecord topRecord = getTopNonFinishingActivity();
info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
}
@@ -4137,9 +3374,9 @@ class Task extends WindowContainer<WindowContainer> {
private @Nullable PictureInPictureParams getPictureInPictureParams(Task top) {
if (top == null) return null;
- final ActivityRecord topVisibleActivity = top.getTopVisibleActivity();
- return (topVisibleActivity == null || topVisibleActivity.pictureInPictureArgs.empty())
- ? null : new PictureInPictureParams(topVisibleActivity.pictureInPictureArgs);
+ final ActivityRecord topMostActivity = top.getTopMostActivity();
+ return (topMostActivity == null || topMostActivity.pictureInPictureArgs.empty())
+ ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
}
private Rect getDisplayCutoutInsets(Task top) {
@@ -4206,184 +3443,6 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
- /**
- * Returns true if the task should be visible.
- *
- * @param starting The currently starting activity or null if there is none.
- */
- boolean shouldBeVisible(ActivityRecord starting) {
- return getVisibility(starting) != TASK_VISIBILITY_INVISIBLE;
- }
-
- /**
- * Returns true if the task should be visible.
- *
- * @param starting The currently starting activity or null if there is none.
- */
- @TaskVisibility
- int getVisibility(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
- return TASK_VISIBILITY_INVISIBLE;
- }
-
- if (isTopActivityLaunchedBehind()) {
- return TASK_VISIBILITY_VISIBLE;
- }
-
- boolean gotRootSplitScreenTask = false;
- boolean gotOpaqueSplitScreenPrimary = false;
- boolean gotOpaqueSplitScreenSecondary = false;
- boolean gotTranslucentFullscreen = false;
- boolean gotTranslucentSplitScreenPrimary = false;
- boolean gotTranslucentSplitScreenSecondary = false;
- boolean shouldBeVisible = true;
-
- // This root task is only considered visible if all its parent root tasks are considered
- // visible, so check the visibility of all ancestor root task first.
- final WindowContainer parent = getParent();
- if (parent.asTask() != null) {
- final int parentVisibility = parent.asTask().getVisibility(starting);
- if (parentVisibility == TASK_VISIBILITY_INVISIBLE) {
- // Can't be visible if parent isn't visible
- return TASK_VISIBILITY_INVISIBLE;
- } else if (parentVisibility == TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
- // Parent is behind a translucent container so the highest visibility this container
- // can get is that.
- gotTranslucentFullscreen = true;
- }
- }
-
- final List<Task> adjacentTasks = new ArrayList<>();
- final int windowingMode = getWindowingMode();
- final boolean isAssistantType = isActivityTypeAssistant();
- for (int i = parent.getChildCount() - 1; i >= 0; --i) {
- final WindowContainer wc = parent.getChildAt(i);
- final Task other = wc.asTask();
- if (other == null) continue;
-
- final boolean hasRunningActivities = other.topRunningActivity() != null;
- if (other == this) {
- // Should be visible if there is no other stack occluding it, unless it doesn't
- // have any running activities, not starting one and not home stack.
- shouldBeVisible = hasRunningActivities || isInTask(starting) != null
- || isActivityTypeHome();
- break;
- }
-
- if (!hasRunningActivities) {
- continue;
- }
-
- final int otherWindowingMode = other.getWindowingMode();
-
- if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- if (other.isTranslucent(starting)) {
- // Can be visible behind a translucent fullscreen stack.
- gotTranslucentFullscreen = true;
- continue;
- }
- return TASK_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
- && other.matchParentBounds()) {
- if (other.isTranslucent(starting)) {
- // Can be visible behind a translucent task.
- gotTranslucentFullscreen = true;
- continue;
- }
- // Multi-window task that matches parent bounds would occlude other children.
- return TASK_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && !gotOpaqueSplitScreenPrimary) {
- gotRootSplitScreenTask = true;
- gotTranslucentSplitScreenPrimary = other.isTranslucent(starting);
- gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && gotOpaqueSplitScreenPrimary) {
- // Can not be visible behind another opaque stack in split-screen-primary mode.
- return TASK_VISIBILITY_INVISIBLE;
- }
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && !gotOpaqueSplitScreenSecondary) {
- gotRootSplitScreenTask = true;
- gotTranslucentSplitScreenSecondary = other.isTranslucent(starting);
- gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && gotOpaqueSplitScreenSecondary) {
- // Can not be visible behind another opaque stack in split-screen-secondary mode.
- return TASK_VISIBILITY_INVISIBLE;
- }
- }
- if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
- // Can not be visible if we are in split-screen windowing mode and both halves of
- // the screen are opaque.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (isAssistantType && gotRootSplitScreenTask) {
- // Assistant stack can't be visible behind split-screen. In addition to this not
- // making sense, it also works around an issue here we boost the z-order of the
- // assistant window surfaces in window manager whenever it is visible.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (other.mAdjacentTask != null) {
- if (adjacentTasks.contains(other.mAdjacentTask)) {
- if (other.isTranslucent(starting)
- || other.mAdjacentTask.isTranslucent(starting)) {
- // Can be visible behind a translucent adjacent tasks.
- gotTranslucentFullscreen = true;
- continue;
- }
- // Can not be visible behind adjacent tasks.
- return TASK_VISIBILITY_INVISIBLE;
- } else {
- adjacentTasks.add(other);
- }
- }
- }
-
- if (!shouldBeVisible) {
- return TASK_VISIBILITY_INVISIBLE;
- }
-
- // Handle cases when there can be a translucent split-screen stack on top.
- switch (windowingMode) {
- case WINDOWING_MODE_FULLSCREEN:
- if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
- // At least one of the split-screen stacks that covers this one is translucent.
- // When in split mode, home task will be reparented to the secondary split while
- // leaving tasks not supporting split below. Due to
- // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
- // the bottom, this makes sure tasks not in split roots won't occlude home task
- // unexpectedly.
- return TASK_VISIBILITY_INVISIBLE;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- if (gotTranslucentSplitScreenPrimary) {
- // Covered by translucent primary split-screen on top.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- if (gotTranslucentSplitScreenSecondary) {
- // Covered by translucent secondary split-screen on top.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- }
-
- // Lastly - check if there is a translucent fullscreen stack on top.
- return gotTranslucentFullscreen ? TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
- : TASK_VISIBILITY_VISIBLE;
- }
-
- private boolean isTopActivityLaunchedBehind() {
- final ActivityRecord top = topRunningActivity();
- if (top != null && top.mLaunchTaskBehind) {
- return true;
- }
- return false;
- }
-
ActivityRecord isInTask(ActivityRecord r) {
if (r == null) {
return null;
@@ -4566,7 +3625,7 @@ class Task extends WindowContainer<WindowContainer> {
// Increment the total number of non-finishing activities
numActivities++;
- if (top == null || (top.isState(ActivityState.INITIALIZING))) {
+ if (top == null || (top.isState(INITIALIZING))) {
top = r;
// Reset the number of running activities until we hit the first non-initializing
// activity
@@ -5309,9 +4368,7 @@ class Task extends WindowContainer<WindowContainer> {
return super.isAlwaysOnTop();
}
- /**
- * Returns whether this task is currently forced to be hidden for any reason.
- */
+ @Override
protected boolean isForceHidden() {
return mForceHiddenFlags != 0;
}
@@ -5464,8 +4521,10 @@ class Task extends WindowContainer<WindowContainer> {
mAtmService.continueWindowLayout();
}
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- mRootWindowContainer.resumeFocusedTasksTopActivities();
+ if (!mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
}
void resumeNextFocusAfterReparent() {
@@ -5597,19 +4656,6 @@ class Task extends WindowContainer<WindowContainer> {
r.completeResumeLocked();
}
- void awakeFromSleepingLocked() {
- if (!isLeafTask()) {
- forAllLeafTasks((task) -> task.awakeFromSleepingLocked(),
- true /* traverseTopToBottom */);
- return;
- }
-
- if (mPausingActivity != null) {
- Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
- mPausingActivity.activityPaused(true);
- }
- }
-
void checkReadyForSleep() {
if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -5628,302 +4674,13 @@ class Task extends WindowContainer<WindowContainer> {
* the process of going to sleep (checkReadyForSleep will be called when that process finishes).
*/
boolean goToSleepIfPossible(boolean shuttingDown) {
- if (!isLeafTask()) {
- final int[] sleepInProgress = {0};
- forAllLeafTasks((t) -> {
- if (!t.goToSleepIfPossible(shuttingDown)) {
- sleepInProgress[0]++;
- }
- }, true);
- return sleepInProgress[0] == 0;
- }
-
- boolean shouldSleep = true;
- if (mResumedActivity != null) {
- // Still have something resumed; can't sleep until it is paused.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
- if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
- "Sleep => pause with userLeaving=false");
-
- startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
- "sleep");
- shouldSleep = false ;
- } else if (mPausingActivity != null) {
- // Still waiting for something to pause; can't sleep yet.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
- shouldSleep = false;
- }
-
- if (!shuttingDown) {
- if (containsActivityFromRootTask(mTaskSupervisor.mStoppingActivities)) {
- // Still need to tell some activities to stop; can't sleep yet.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
- mTaskSupervisor.mStoppingActivities.size());
-
- mTaskSupervisor.scheduleIdle();
- shouldSleep = false;
- }
- }
-
- if (shouldSleep) {
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- }
-
- return shouldSleep;
- }
-
- private boolean containsActivityFromRootTask(List<ActivityRecord> rs) {
- for (ActivityRecord r : rs) {
- if (r.getRootTask() == this) {
- return true;
- }
- }
- return false;
- }
-
- final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) {
- return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
- }
-
- /**
- * Start pausing the currently resumed activity. It is an error to call this if there
- * is already an activity being paused or there is no resumed activity.
- *
- * @param userLeaving True if this should result in an onUserLeaving to the current activity.
- * @param uiSleeping True if this is happening with the user interface going to sleep (the
- * screen turning off).
- * @param resuming The activity we are currently trying to resume or null if this is not being
- * called as part of resuming the top activity, so we shouldn't try to instigate
- * a resume here if not null.
- * @param reason The reason of pausing the activity.
- * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
- * it to tell us when it is done.
- */
- final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
- ActivityRecord resuming, String reason) {
- if (!isLeafTask()) {
- final int[] pausing = {0};
- forAllLeafTasks((t) -> {
- if (t.startPausingLocked(userLeaving, uiSleeping, resuming, reason)) {
- pausing[0]++;
- }
- }, true /* traverseTopToBottom */);
- return pausing[0] > 0;
- }
-
- if (mPausingActivity != null) {
- Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
- + " state=" + mPausingActivity.getState());
- if (!shouldSleepActivities()) {
- // Avoid recursion among check for sleep and complete pause during sleeping.
- // Because activity will be paused immediately after resume, just let pause
- // be completed by the order of activity paused from clients.
- completePauseLocked(false, resuming);
- }
- }
- ActivityRecord prev = mResumedActivity;
-
- if (prev == null) {
- if (resuming == null) {
- Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- return false;
- }
-
- if (prev == resuming) {
- Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
- return false;
- }
-
- ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
- mTaskSupervisor.mNoHistoryActivities.add(prev);
- }
- prev.setState(PAUSING, "startPausingLocked");
- prev.getTask().touchActiveTime();
-
- mAtmService.updateCpuStats();
-
- boolean pauseImmediately = false;
- boolean shouldAutoPip = false;
- if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
- // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
- // activity to be paused, while at the same time resuming the new resume activity
- // only if the previous activity can't go into Pip since we want to give Pip
- // activities a chance to enter Pip before resuming the next activity.
- final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
- "shouldResumeWhilePausing", userLeaving);
- if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
- shouldAutoPip = true;
- } else if (!lastResumedCanPip) {
- pauseImmediately = true;
- } else {
- // The previous activity may still enter PIP even though it did not allow auto-PIP.
- }
- }
-
- boolean didAutoPip = false;
- if (prev.attachedToProcess()) {
- if (shouldAutoPip) {
- ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
- + "directly: %s", prev);
-
- didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
- mPausingActivity = null;
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
- try {
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving, reason);
-
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
- mTaskSupervisor.acquireLaunchWakelock();
- }
-
- // If already entered PIP mode, no need to keep pausing.
- if (mPausingActivity != null && !didAutoPip) {
- // Have the window manager pause its key dispatching until the new
- // activity has started. If we're pausing the activity just because
- // the screen is being turned off and the UI is sleeping, don't interrupt
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
- }
-
- if (pauseImmediately) {
- // If the caller said they don't want to wait for the pause, then complete
- // the pause now.
- completePauseLocked(false, resuming);
- return false;
-
- } else {
- prev.schedulePauseTimeout();
- return true;
+ final int[] sleepInProgress = {0};
+ forAllLeafTasksAndLeafTaskFragments(taskFragment -> {
+ if (!taskFragment.sleepIfPossible(shuttingDown)) {
+ sleepInProgress[0]++;
}
-
- } else {
- // This activity either failed to schedule the pause or it entered PIP mode,
- // so just treat it as being paused now.
- ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
- if (resuming == null) {
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- return false;
- }
- }
-
- @VisibleForTesting
- void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
- // Complete the pausing process of a pausing activity, so it doesn't make sense to
- // operate on non-leaf tasks.
- warnForNonLeafTask("completePauseLocked");
-
- ActivityRecord prev = mPausingActivity;
- ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
-
- if (prev != null) {
- prev.setWillCloseOrEnterPip(false);
- final boolean wasStopping = prev.isState(STOPPING);
- prev.setState(PAUSED, "completePausedLocked");
- if (prev.finishing) {
- // We will update the activity visibility later, no need to do in
- // completeFinishing(). Updating visibility here might also making the next
- // activities to be resumed, and could result in wrong app transition due to
- // lack of previous activity information.
- ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
- prev = prev.completeFinishing(false /* updateVisibility */,
- "completePausedLocked");
- } else if (prev.hasProcess()) {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
- + "wasStopping=%b visibleRequested=%b", prev, wasStopping,
- prev.mVisibleRequested);
- if (prev.deferRelaunchUntilPaused) {
- // Complete the deferred relaunch that was waiting for pause to complete.
- ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
- prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
- } else if (wasStopping) {
- // We are also stopping, the stop request must have gone soon after the pause.
- // We can't clobber it, because the stop confirmation will not be handled.
- // We don't need to schedule another stop, we only need to let it happen.
- prev.setState(STOPPING, "completePausedLocked");
- } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
- // Clear out any deferred client hide we might currently have.
- prev.setDeferHidingClient(false);
- // If we were visible then resumeTopActivities will release resources before
- // stopping.
- prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
- "completePauseLocked");
- }
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
- prev = null;
- }
- // It is possible the activity was freezing the screen before it was paused.
- // In that case go ahead and remove the freeze this activity has on the screen
- // since it is no longer visible.
- if (prev != null) {
- prev.stopFreezingScreenLocked(true /*force*/);
- }
- mPausingActivity = null;
- }
-
- if (resumeNext) {
- final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
- mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null);
- } else {
- checkReadyForSleep();
- final ActivityRecord top =
- topRootTask != null ? topRootTask.topRunningActivity() : null;
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run, do resume anyway to start
- // something. Also if the top activity on the root task is not the just paused
- // activity, we need to go ahead and resume it to ensure we complete an
- // in-flight app switch.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
-
- mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
-
- // Notify when the task stack has changed, but only if visibilities changed (not just
- // focus). Also if there is an active root pinned task - we always want to notify it about
- // task stack changes, because its positioning may depend on it.
- if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
- || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
- mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
- mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
- }
+ }, true /* traverseTopToBottom */);
+ return sleepInProgress[0] == 0;
}
boolean isTopRootTaskInDisplayArea() {
@@ -5946,10 +4703,10 @@ class Task extends WindowContainer<WindowContainer> {
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * configuration in {@link EnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
+ * {@link EnsureActivitiesVisibleHelper}.
*
*/
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
@@ -5965,21 +4722,22 @@ class Task extends WindowContainer<WindowContainer> {
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param notifyClients Flag indicating whether the visibility updates should be sent to the
- * clients in {@link mEnsureActivitiesVisibleHelper}.
+ * clients in {@link EnsureActivitiesVisibleHelper}.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * configuration in {@link EnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
+ * {@link EnsureActivitiesVisibleHelper}.
*/
// TODO: Should be re-worked based on the fact that each task as a root task in most cases.
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
- forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process(
- starting, configChanges, preserveWindows, notifyClients),
- true /* traverseTopToBottom */);
+ forAllLeafTasks(task -> {
+ task.updateActivityVisibilities(starting, configChanges, preserveWindows,
+ notifyClients);
+ }, true /* traverseTopToBottom */);
// Notify WM shell that task visibilities may have changed
forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
@@ -6054,25 +4812,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /** @see ActivityRecord#cancelInitializing() */
- void cancelInitializingActivities() {
- // We don't want to clear starting window for activities that aren't behind fullscreen
- // activities as we need to display their starting window until they are done initializing.
- checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
- }
-
- /**
- * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
- * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
- * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
- * activities to the function.
- */
- boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- return mCheckBehindFullscreenActivityHelper.process(
- toCheck, handleBehindFullscreenActivity);
- }
-
/**
* Ensure that the top activity in the root task is resumed.
*
@@ -6113,7 +4852,8 @@ class Task extends WindowContainer<WindowContainer> {
if (!child.isTopActivityFocusable()) {
continue;
}
- if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
+ if (child.getVisibility(null /* starting */)
+ != TASK_FRAGMENT_VISIBILITY_VISIBLE) {
break;
}
@@ -6160,383 +4900,25 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
- // Find the next top-most activity to resume in this root task that is not finishing and is
- // focusable. If it is not focusable, we will fall into the case below to resume the
- // top activity in the next focusable task.
- ActivityRecord next = topRunningActivity(true /* focusableOnly */);
-
- final boolean hasRunningActivity = next != null;
-
- // TODO: Maybe this entire condition can get removed?
- if (hasRunningActivity && !isAttached()) {
- return false;
- }
-
mRootWindowContainer.cancelInitializingActivities();
- if (!hasRunningActivity) {
- // There are no activities left in the root task, let's look somewhere else.
+ final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);
+ if (topActivity == null) {
+ // There are no activities left in this task, let's look somewhere else.
return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
}
- next.delayedResume = false;
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
- // we still want to check if the visibility of other windows have changed (e.g. bringing
- // a fullscreen window forward to cover another freeform activity.)
- if (taskDisplayArea.inMultiWindowMode()) {
- taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, true /* notifyClients */);
- }
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity "
- + "resumed %s", next);
- return false;
- }
-
- if (!next.canResumeByCompat()) {
- return false;
- }
-
- // If we are currently pausing an activity, then don't do anything until that is done.
- final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
- if (!allPausedComplete) {
- ProtoLog.v(WM_DEBUG_STATES,
- "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top activity is paused,
- // well that is the state we want.
- if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Going to sleep and"
- + " all paused");
- return false;
- }
-
- // Make sure that the user who owns this activity is started. If not,
- // we will just leave it as is because someone should be bringing
- // another user's activities to the top of the stack.
- if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
- Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.mUserId + " is stopped");
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mTaskSupervisor.mStoppingActivities.remove(next);
-
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
-
- mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
-
- ActivityRecord lastResumed = null;
- final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
- if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) {
- // So, why aren't we using prev here??? See the param comment on the method. prev
- // doesn't represent the last resumed activity. However, the last focus stack does if
- // it isn't null.
- lastResumed = lastFocusedRootTask.getResumedActivity();
- }
-
- boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
- if (mResumedActivity != null) {
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
- pausing |= startPausingLocked(false /* uiSleeping */, next,
- "resumeTopActivityInnerLocked");
- }
- if (pausing) {
- ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivityLocked: Skip resume: need to"
- + " start pausing");
- // At this point we want to put the upcoming activity's process
- // at the top of the LRU list, since we know we will be needing it
- // very soon and it would be a waste to let it get killed if it
- // happens to be sitting towards the end.
- if (next.attachedToProcess()) {
- next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
- true /* activityChange */, false /* updateOomAdj */,
- false /* addPendingTopUid */);
- } else if (!next.isProcessRunning()) {
- // Since the start-process is asynchronous, if we already know the process of next
- // activity isn't running, we can start the process earlier to save the time to wait
- // for the current activity to be paused.
- final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
- mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
- isTop ? "pre-top-activity" : "pre-activity");
- }
- if (lastResumed != null) {
- lastResumed.setWillCloseOrEnterPip(true);
- }
- return true;
- } else if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // It is possible for the activity to be resumed when we paused back stacks above if the
- // next activity doesn't have to wait for pause to complete.
- // So, nothing else to-do except:
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity resumed "
- + "(dontWaitForPause) %s", next);
- return true;
- }
-
- // If the most recent activity was noHistory but was only stopped rather
- // than stopped+finished because the device went to sleep, we need to make
- // sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities()) {
- mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
- }
-
- if (prev != null && prev != next && next.nowVisible) {
-
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- prev.setVisibility(false);
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Not waiting for visible to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Previous already visible but still waiting to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- }
-
- }
-
- // Launching this app's activity, make sure the app is no longer
- // considered stopped.
- try {
- mTaskSupervisor.getActivityMetricsLogger()
- .notifyBeforePackageUnstopped(next.packageName);
- mAtmService.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
- } catch (RemoteException e1) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + next.packageName + ": " + e);
- }
-
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- boolean anim = true;
- final DisplayContent dc = taskDisplayArea.mDisplayContent;
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare close transition: prev=" + prev);
- if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_CLOSE);
- }
- prev.setVisibility(false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: prev=" + prev);
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN,
- next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
- }
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN);
- }
- }
-
- if (anim) {
- next.applyOptionsAnimation();
- } else {
- next.abortAndClearOptionsAnimation();
- }
-
- mTaskSupervisor.mNoAnimActivities.clear();
-
- if (next.attachedToProcess()) {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
- + " stopped=" + next.stopped
- + " visibleRequested=" + next.mVisibleRequested);
-
- // If the previous activity is translucent, force a visibility update of
- // the next activity, so that it's added to WM's opening app list, and
- // transition animation can be set up properly.
- // For example, pressing Home button with a translucent activity in focus.
- // Launcher is already visible in this case. If we don't add it to opening
- // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
- // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
- final boolean lastActivityTranslucent = lastFocusedRootTask != null
- && (lastFocusedRootTask.inMultiWindowMode()
- || (lastFocusedRootTask.mLastPausedActivity != null
- && !lastFocusedRootTask.mLastPausedActivity.occludesParent()));
-
- // This activity is now becoming visible.
- if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
-
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
-
- ActivityRecord lastResumedActivity =
- lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity();
- final ActivityState lastState = next.getState();
-
- mAtmService.updateCpuStats();
-
- ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
-
- next.setState(RESUMED, "resumeTopActivityInnerLocked");
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
-
- // Activity should also be visible if set mLaunchTaskBehind to true (see
- // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
- if (shouldBeVisible(next)) {
- // We have special rotation behavior when here is some active activity that
- // requests specific orientation or Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed
- // to get the correct rotation behavior. Otherwise the following call to update
- // the orientation may cause incorrect configurations delivered to client as a
- // result of invisible window resize.
- // TODO: Remove this once visibilities are set correctly immediately when
- // starting an activity.
- notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
- true /* markFrozenIfConfigChanged */, false /* deferResume */);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivity();
- ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
- + "%s, new next: %s", next, nextNext);
- if (nextNext != next) {
- // Do over!
- mTaskSupervisor.scheduleResumeTopActivities();
- }
- if (!next.mVisibleRequested || next.stopped) {
- next.setVisibility(true);
- }
- next.completeResumeLocked();
- return true;
- }
-
- try {
- final ClientTransaction transaction =
- ClientTransaction.obtain(next.app.getThread(), next.appToken);
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- transaction.addCallback(ActivityResultItem.obtain(a));
- }
- }
-
- if (next.newIntents != null) {
- transaction.addCallback(
- NewIntentItem.obtain(next.newIntents, true /* resume */));
- }
-
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
-
- EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
- next.getTask().mTaskId, next.shortComponentName);
-
- mAtmService.getAppWarningsLocked().onResumeActivity(next);
- next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
- next.abortAndClearOptionsAnimation();
- transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.app.getReportedProcState(),
- dc.isNextTransitionForward()));
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
-
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
- + "%s", lastState, next);
- next.setState(lastState, "resumeTopActivityInnerLocked");
-
- // lastResumedActivity being non-null implies there is a lastStack present.
- if (lastResumedActivity != null) {
- lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
- }
-
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
- && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
- next.showStartingWindow(false /* taskSwitch */);
- }
- mTaskSupervisor.startSpecificActivity(next, true, false);
- return true;
+ final boolean[] resumed = new boolean[1];
+ final TaskFragment topFragment = topActivity.getTaskFragment();
+ resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
+ forAllLeafTaskFragments(f -> {
+ if (topFragment == f) {
+ return;
}
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.completeResumeLocked();
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- next.finishIfPossible("resume-exception", true /* oomAdj */);
- return true;
- }
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_PREVIEW) {
- next.showStartingWindow(false /* taskSwich */);
- }
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
- }
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Restarting %s", next);
- mTaskSupervisor.startSpecificActivity(next, true, true);
- }
-
- return true;
+ resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
+ }, true);
+ return resumed[0];
}
/**
@@ -6616,7 +4998,6 @@ class Task extends WindowContainer<WindowContainer> {
// Slot the activity into the history root task and proceed
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
+ "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
- task.positionChildAtTop(r);
// The transition animation and starting window are not needed if {@code allowMoveToFront}
// is false, because the activity won't be visible.
@@ -7217,13 +5598,6 @@ class Task extends WindowContainer<WindowContainer> {
return true;
}
- /**
- * Ensures all visible activities at or below the input activity have the right configuration.
- */
- void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
- mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
- }
-
// TODO: Can only be called from special methods in ActivityTaskSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
@@ -7277,114 +5651,35 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /**
- * Reset local parameters because an app's activity died.
- * @param app The app of the activity that died.
- * @return {@code true} if the process of the pausing activity is died.
- */
- boolean handleAppDied(WindowProcessController app) {
- warnForNonLeafTask("handleAppDied");
- boolean isPausingDied = false;
- if (mPausingActivity != null && mPausingActivity.app == app) {
- ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
- mPausingActivity);
- mPausingActivity = null;
- isPausingDied = true;
- }
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- if (mLastPausedActivity.isNoHistory()) {
- mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
- }
- mLastPausedActivity = null;
- }
- return isPausingDied;
- }
-
boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
String dumpPackage, final boolean needSep) {
- Runnable headerPrinter = () -> {
- if (needSep) {
- pw.println();
- }
- pw.println(" RootTask #" + getRootTaskId()
- + ": type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()));
- pw.println(" isSleeping=" + shouldSleepActivities());
- pw.println(" mBounds=" + getRequestedOverrideBounds());
- pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer);
- };
-
- boolean printed = false;
+ return dump(" ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */);
+ }
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no tasks/activities inside.
- headerPrinter.run();
- headerPrinter = null;
- printed = true;
+ @Override
+ void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ pw.print(prefix); pw.print("* "); pw.println(this);
+ pw.println(prefix + " mBounds=" + getRequestedOverrideBounds());
+ pw.println(prefix + " mCreatedByOrganizer=" + mCreatedByOrganizer);
+ if (mLastNonFullscreenBounds != null) {
+ pw.print(prefix); pw.print(" mLastNonFullscreenBounds=");
+ pw.println(mLastNonFullscreenBounds);
}
-
- printed |= printThisActivity(pw, getPausingActivity(), dumpPackage, false,
- " mPausingActivity: ", null);
- printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false,
- " mResumedActivity: ", null);
if (dumpAll) {
- printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
- " mLastPausedActivity: ", null);
+ printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+ prefix + " mLastPausedActivity: ", null);
}
-
- printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
-
- return printed;
- }
-
- private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
- if (!hasChild()) {
- return false;
+ if (isLeafTask()) {
+ pw.println(prefix + " isSleeping=" + shouldSleepActivities());
+ printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
+ prefix + " topPausingActivity=", null);
+ printThisActivity(pw, getTopResumedActivity(), dumpPackage, false,
+ prefix + " topResumedActivity=", null);
+ if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) {
+ pw.print(prefix); pw.print(" mMinWidth="); pw.print(mMinWidth);
+ pw.print(" mMinHeight="); pw.println(mMinHeight);
+ }
}
- final AtomicBoolean printedHeader = new AtomicBoolean(false);
- final AtomicBoolean printed = new AtomicBoolean(false);
- forAllLeafTasks((task) -> {
- final String prefix = " ";
- Runnable headerPrinter = () -> {
- printed.set(true);
- if (!printedHeader.get()) {
- if (needSep) {
- pw.println("");
- }
- if (header != null) {
- header.run();
- }
- printedHeader.set(true);
- }
- pw.print(prefix); pw.print("* "); pw.println(task);
- pw.print(prefix); pw.print(" mBounds=");
- pw.println(task.getRequestedOverrideBounds());
- pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth);
- pw.print(" mMinHeight="); pw.println(task.mMinHeight);
- if (mLastNonFullscreenBounds != null) {
- pw.print(prefix);
- pw.print(" mLastNonFullscreenBounds=");
- pw.println(task.mLastNonFullscreenBounds);
- }
- task.dump(pw, prefix + " ");
- };
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no activities inside.
- headerPrinter.run();
- headerPrinter = null;
- }
- final ArrayList<ActivityRecord> activities = new ArrayList<>();
- // Add activities by traversing the hierarchy from bottom to top, since activities
- // are dumped in reverse order in {@link ActivityTaskSupervisor#dumpHistoryList()}.
- task.forAllActivities((Consumer<ActivityRecord>) activities::add,
- false /* traverseTopToBottom */);
- dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
- dumpPackage, false, headerPrinter, task);
- }, true /* traverseTopToBottom */);
- return printed.get();
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -7769,7 +6064,8 @@ class Task extends WindowContainer<WindowContainer> {
/** Returns true if a removal action is still being deferred. */
boolean handleCompleteDeferredRemoval() {
- if (isAnimating(TRANSITION | CHILDREN)) {
+ if (isAnimating(TRANSITION | CHILDREN)
+ || mAtmService.getTransitionController().inTransition(this)) {
return true;
}
@@ -7784,6 +6080,7 @@ class Task extends WindowContainer<WindowContainer> {
return mAnimatingActivityRegistry;
}
+ @Override
void executeAppTransition(ActivityOptions options) {
mDisplayContent.executeAppTransition();
ActivityOptions.abort(options);
@@ -7806,10 +6103,6 @@ class Task extends WindowContainer<WindowContainer> {
return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
}
- boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mAtmService.mShuttingDown;
- }
-
private Rect getRawBounds() {
return super.getBounds();
}
@@ -7828,14 +6121,12 @@ class Task extends WindowContainer<WindowContainer> {
}
final long token = proto.start(fieldId);
- super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
proto.write(TaskProto.ID, mTaskId);
- proto.write(DISPLAY_ID, getDisplayId());
proto.write(ROOT_TASK_ID, getRootTaskId());
- if (mResumedActivity != null) {
- mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ if (getTopResumedActivity() != null) {
+ getTopResumedActivity().writeIdentifierToProto(proto, RESUMED_ACTIVITY);
}
if (realActivity != null) {
proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
@@ -7843,11 +6134,7 @@ class Task extends WindowContainer<WindowContainer> {
if (origActivity != null) {
proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
}
- proto.write(ACTIVITY_TYPE, getActivityType());
proto.write(RESIZE_MODE, mResizeMode);
- proto.write(MIN_WIDTH, mMinWidth);
- proto.write(MIN_HEIGHT, mMinHeight);
-
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().dumpDebug(proto, BOUNDS);
@@ -7864,6 +6151,8 @@ class Task extends WindowContainer<WindowContainer> {
proto.write(AFFINITY, affinity);
proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null);
+ super.dumpDebug(proto, TASK_FRAGMENT, logLevel);
+
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index d450dbffe4a1..10c16ff96a16 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -34,11 +34,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -469,7 +468,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Update the top resumed activity because the preferred top focusable task may be changed.
mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded();
- final ActivityRecord r = child.getResumedActivity();
+ final ActivityRecord r = child.getTopResumedActivity();
if (r != null && r == mRootWindowContainer.getTopResumedActivity()) {
mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt");
}
@@ -1231,7 +1230,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
+ adjacentFlagRootTask);
}
- if (adjacentFlagRootTask.mAdjacentTask == null) {
+ if (adjacentFlagRootTask.getAdjacentTaskFragment() == null) {
throw new UnsupportedOperationException(
"Can't set non-adjacent root as launch adjacent flag root tr="
+ adjacentFlagRootTask);
@@ -1269,8 +1268,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// If the adjacent launch is coming from the same root, launch to adjacent root instead.
if (sourceTask != null
&& sourceTask.getRootTask().mTaskId == mLaunchAdjacentFlagRootTask.mTaskId
- && mLaunchAdjacentFlagRootTask.mAdjacentTask != null) {
- return mLaunchAdjacentFlagRootTask.mAdjacentTask;
+ && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null) {
+ return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
} else {
return mLaunchAdjacentFlagRootTask;
}
@@ -1280,8 +1279,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
// Return the focusable root task for improving the UX with staged split screen.
- final Task adjacentRootTask = launchRootTask != null
- ? launchRootTask.mAdjacentTask : null;
+ final TaskFragment adjacentTaskFragment = launchRootTask != null
+ ? launchRootTask.getAdjacentTaskFragment() : null;
+ final Task adjacentRootTask =
+ adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
return adjacentRootTask;
} else {
@@ -1373,11 +1374,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
// TODO(b/111541062): Move this into Task#getResumedActivity()
// Check if the focused root task has the resumed activity
- ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity == null || resumedActivity.app == null) {
// If there is no registered resumed activity in the root task or it is not running -
// try to use previously resumed one.
- resumedActivity = focusedRootTask.getPausingActivity();
+ resumedActivity = focusedRootTask.getTopPausingActivity();
if (resumedActivity == null || resumedActivity.app == null) {
// If previously resumed activity doesn't work either - find the topmost running
// activity that can be focused.
@@ -1404,7 +1405,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Clear last paused activity if focused root task changed while sleeping, so that the
// top activity of current focused task can be resumed.
if (mDisplayContent.isSleeping()) {
- currentFocusedTask.mLastPausedActivity = null;
+ currentFocusedTask.clearLastPausedActivity();
}
mLastFocusedRootTask = prevFocusedTask;
@@ -1425,7 +1426,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
continue;
}
- final ActivityRecord r = mChildren.get(i).asTask().getResumedActivity();
+ final ActivityRecord r = mChildren.get(i).asTask().getTopResumedActivity();
if (r != null && !r.isState(RESUMED)) {
return false;
}
@@ -1451,18 +1452,30 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
*/
boolean pauseBackTasks(ActivityRecord resuming) {
final int[] someActivityPaused = {0};
- forAllLeafTasks((task) -> {
- final ActivityRecord resumedActivity = task.getResumedActivity();
- if (resumedActivity != null
- && (task.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE
- || !task.isTopActivityFocusable())) {
- ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s "
- + "mResumedActivity=%s", task, resumedActivity);
- if (task.startPausingLocked(false /* uiSleeping*/,
- resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
+ forAllLeafTasks(leafTask -> {
+ // Check if the direct child resumed activity in the leaf task needed to be paused if
+ // the leaf task is not a leaf task fragment.
+ if (!leafTask.isLeafTaskFragment()) {
+ final ActivityRecord top = topRunningActivity();
+ final ActivityRecord resumedActivity = leafTask.getResumedActivity();
+ if (resumedActivity != null && top.getTaskFragment() != leafTask) {
+ // Pausing the resumed activity because it is occluded by other task fragment.
+ if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
+ }
}
}
+
+ leafTask.forAllLeafTaskFragments((taskFrag) -> {
+ final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+ if (resumedActivity != null
+ && (taskFrag.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE
+ || !taskFrag.isTopActivityFocusable())) {
+ if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
+ }
+ }
+ }, true /* traverseTopToBottom */);
}, true /* traverseTopToBottom */);
return someActivityPaused[0] > 0;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
new file mode 100644
index 000000000000..12ad634fb6b8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -0,0 +1,2142 @@
+/*
+ * 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.wm;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.os.UserHandle.USER_NULL;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.TaskFragmentProto.ACTIVITY_TYPE;
+import static com.android.server.wm.TaskFragmentProto.DISPLAY_ID;
+import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
+import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
+import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.ResultInfo;
+import android.app.WindowConfiguration;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A basic container that can be used to contain activities or other {@link TaskFragment}, which
+ * also able to manage the activity lifecycle and updates the visibilities of the activities in it.
+ */
+class TaskFragment extends WindowContainer<WindowContainer> {
+ @IntDef(prefix = {"TASK_FRAGMENT_VISIBILITY"}, value = {
+ TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ })
+ @interface TaskFragmentVisibility {}
+
+ /**
+ * TaskFragment is visible. No other TaskFragment(s) on top that fully or partially occlude it.
+ */
+ static final int TASK_FRAGMENT_VISIBILITY_VISIBLE = 0;
+
+ /** TaskFragment is partially occluded by other translucent TaskFragment(s) on top of it. */
+ static final int TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
+
+ /** TaskFragment is completely invisible. */
+ static final int TASK_FRAGMENT_VISIBILITY_INVISIBLE = 2;
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskFragment" : TAG_ATM;
+ private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+ private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+
+ /** Set to false to disable the preview that is shown while a new activity is being started. */
+ static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+ /**
+ * Indicate that the minimal width/height should use the default value.
+ *
+ * @see #mMinWidth
+ * @see #mMinHeight
+ */
+ static final int INVALID_MIN_SIZE = -1;
+
+ final ActivityTaskManagerService mAtmService;
+ final ActivityTaskSupervisor mTaskSupervisor;
+ final RootWindowContainer mRootWindowContainer;
+ private final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
+
+ /**
+ * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+ * should use the default minimal width.
+ */
+ int mMinWidth;
+
+ /**
+ * Minimal height of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+ * should use the default minimal height.
+ */
+ int mMinHeight;
+
+ /** Avoid reentrant of {@link #removeImmediately()}. */
+ private boolean mRemoving;
+
+ // The TaskFragment that adjacent to this one.
+ private TaskFragment mAdjacentTaskFragment;
+
+ /**
+ * When we are in the process of pausing an activity, before starting the
+ * next one, this variable holds the activity that is currently being paused.
+ *
+ * Only set at leaf task fragments.
+ */
+ @Nullable
+ private ActivityRecord mPausingActivity = null;
+
+ /**
+ * This is the last activity that we put into the paused state. This is
+ * used to determine if we need to do an activity transition while sleeping,
+ * when we normally hold the top activity paused.
+ */
+ ActivityRecord mLastPausedActivity = null;
+
+ /**
+ * Current activity that is resumed, or null if there is none.
+ * Only set at leaf task fragments.
+ */
+ @Nullable
+ private ActivityRecord mResumedActivity = null;
+
+ /**
+ * This TaskFragment was created by an organizer which has the following implementations.
+ * <ul>
+ * <li>The TaskFragment won't be removed when it is empty. Removal has to be an explicit
+ * request from the organizer.</li>
+ * <li>If this fragment is a Task object then unlike other non-root tasks, it's direct
+ * children are visible to the organizer for ordering purposes.</li>
+ * <li>A TaskFragment can be created by {@link android.window.TaskFragmentOrganizer}, and
+ * a Task can be created by {@link android.window.TaskOrganizer}.</li>
+ * </ul>
+ */
+ @VisibleForTesting
+ boolean mCreatedByOrganizer;
+
+ /** Organizer that organizing this TaskFragment. */
+ @Nullable
+ private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+
+ /** Client assigned unique token for this TaskFragment if this is created by an organizer. */
+ @Nullable
+ private IBinder mFragmentToken;
+
+ /**
+ * The PID of the organizer that created this TaskFragment. It should be the same as the PID
+ * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
+ * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment.
+ */
+ private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;
+
+ private final Rect mTmpInsets = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpFullBounds = new Rect();
+ private final Rect mTmpStableBounds = new Rect();
+ private final Rect mTmpNonDecorBounds = new Rect();
+
+ private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
+ new EnsureActivitiesVisibleHelper(this);
+ private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+ new EnsureVisibleActivitiesConfigHelper();
+ private class EnsureVisibleActivitiesConfigHelper {
+ private boolean mUpdateConfig;
+ private boolean mPreserveWindow;
+ private boolean mBehindFullscreen;
+
+ void reset(boolean preserveWindow) {
+ mPreserveWindow = preserveWindow;
+ mUpdateConfig = false;
+ mBehindFullscreen = false;
+ }
+
+ void process(ActivityRecord start, boolean preserveWindow) {
+ if (start == null || !start.mVisibleRequested) {
+ return;
+ }
+ reset(preserveWindow);
+
+ final PooledFunction f = PooledLambda.obtainFunction(
+ EnsureVisibleActivitiesConfigHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+ f.recycle();
+
+ if (mUpdateConfig) {
+ // Ensure the resumed state of the focus activity if we updated the configuration of
+ // any activity.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ }
+
+ boolean processActivity(ActivityRecord r) {
+ mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+ mBehindFullscreen |= r.occludesParent();
+ return mBehindFullscreen;
+ }
+ }
+
+ TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
+ boolean createdByOrganizer) {
+ super(atmService.mWindowManager);
+
+ mAtmService = atmService;
+ mTaskSupervisor = mAtmService.mTaskSupervisor;
+ mRootWindowContainer = mAtmService.mRootWindowContainer;
+ mCreatedByOrganizer = createdByOrganizer;
+ mTaskFragmentOrganizerController =
+ mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mFragmentToken = fragmentToken;
+ mRemoteToken = new RemoteToken(this);
+ }
+
+ void setAdjacentTaskFragment(TaskFragment taskFragment) {
+ mAdjacentTaskFragment = taskFragment;
+ taskFragment.mAdjacentTaskFragment = this;
+ }
+
+ void setTaskFragmentOrganizer(ITaskFragmentOrganizer organizer, int pid) {
+ mTaskFragmentOrganizer = organizer;
+ mTaskFragmentOrganizerPid = pid;
+ }
+
+ TaskFragment getAdjacentTaskFragment() {
+ return mAdjacentTaskFragment;
+ }
+
+ /** @return the currently resumed activity. */
+ ActivityRecord getResumedActivity() {
+ return mResumedActivity;
+ }
+
+ void setResumedActivity(ActivityRecord r, String reason) {
+ warnForNonLeafTaskFragment("setResumedActivity");
+ if (mResumedActivity == r) {
+ return;
+ }
+
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
+ + mResumedActivity + " to:" + r + " reason:" + reason);
+ }
+ mResumedActivity = r;
+ mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ }
+
+ @VisibleForTesting
+ void setPausingActivity(ActivityRecord pausing) {
+ mPausingActivity = pausing;
+ }
+
+ ActivityRecord getPausingActivity() {
+ return mPausingActivity;
+ }
+
+ int getDisplayId() {
+ final DisplayContent dc = getDisplayContent();
+ return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
+ }
+
+ @Nullable
+ Task getTask() {
+ if (asTask() != null) {
+ return asTask();
+ }
+
+ TaskFragment parent = getParent() != null ? getParent().asTaskFragment() : null;
+ return parent != null ? parent.getTask() : null;
+ }
+
+ @Override
+ @Nullable
+ TaskDisplayArea getDisplayArea() {
+ return (TaskDisplayArea) super.getDisplayArea();
+ }
+
+ @Override
+ public boolean isAttached() {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ return taskDisplayArea != null && !taskDisplayArea.isRemoved();
+ }
+
+ /**
+ * Returns the root {@link TaskFragment}, which is usually also a {@link Task}.
+ */
+ @NonNull
+ TaskFragment getRootTaskFragment() {
+ final WindowContainer parent = getParent();
+ if (parent == null) return this;
+
+ final TaskFragment parentTaskFragment = parent.asTaskFragment();
+ return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
+ }
+
+ @Override
+ TaskFragment asTaskFragment() {
+ return this;
+ }
+
+
+ /**
+ * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
+ */
+ private void warnForNonLeafTaskFragment(String func) {
+ if (!isLeafTaskFragment()) {
+ Slog.w(TAG, func + " on non-leaf task fragment " + this);
+ }
+ }
+
+ boolean hasDirectChildActivities() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).asActivityRecord() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void cleanUpActivityReferences(@NonNull ActivityRecord r) {
+ if (mPausingActivity != null && mPausingActivity == r) {
+ mPausingActivity = null;
+ }
+
+ if (mResumedActivity != null && mResumedActivity == r) {
+ setResumedActivity(null, "cleanUpActivityReferences");
+ }
+ r.removeTimeouts();
+ }
+
+ /**
+ * Returns whether this TaskFragment is currently forced to be hidden for any reason.
+ */
+ protected boolean isForceHidden() {
+ return false;
+ }
+
+ boolean isLeafTaskFragment() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).asTaskFragment() != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This should be called when an child activity changes state. This should only
+ * be called from
+ * {@link ActivityRecord#setState(ActivityRecord.State, String)} .
+ * @param record The {@link ActivityRecord} whose state has changed.
+ * @param state The new state.
+ * @param reason The reason for the change.
+ */
+ void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
+ String reason) {
+ warnForNonLeafTaskFragment("onActivityStateChanged");
+ if (record == mResumedActivity && state != RESUMED) {
+ setResumedActivity(null, reason + " - onActivityStateChanged");
+ }
+
+ if (state == RESUMED) {
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason);
+ }
+ setResumedActivity(record, reason + " - onActivityStateChanged");
+ if (record == mRootWindowContainer.getTopResumedActivity()) {
+ mAtmService.setResumedActivityUncheckLocked(record, reason);
+ }
+ mTaskSupervisor.mRecentTasks.add(record.getTask());
+ }
+ }
+
+ /**
+ * Resets local parameters because an app's activity died.
+ * @param app The app of the activity that died.
+ * @return {@code true} if the process of the pausing activity is died.
+ */
+ boolean handleAppDied(WindowProcessController app) {
+ warnForNonLeafTaskFragment("handleAppDied");
+ boolean isPausingDied = false;
+ if (mPausingActivity != null && mPausingActivity.app == app) {
+ ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
+ mPausingActivity);
+ mPausingActivity = null;
+ isPausingDied = true;
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ if (mLastPausedActivity.isNoHistory()) {
+ mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
+ }
+ mLastPausedActivity = null;
+ }
+ return isPausingDied;
+ }
+
+ void awakeFromSleeping() {
+ if (mPausingActivity != null) {
+ Slog.d(TAG, "awakeFromSleeping: previously pausing activity didn't pause");
+ mPausingActivity.activityPaused(true);
+ }
+ }
+
+ /**
+ * Tries to put the activities in the task fragment to sleep.
+ *
+ * If the task fragment is not in a state where its activities can be put to sleep, this
+ * function will start any necessary actions to move the task fragment into such a state.
+ * It is expected that this function get called again when those actions complete.
+ *
+ * @param shuttingDown {@code true} when the called because the device is shutting down.
+ * @return true if the root task finished going to sleep, false if the root task only started
+ * the process of going to sleep (checkReadyForSleep will be called when that process finishes).
+ */
+ boolean sleepIfPossible(boolean shuttingDown) {
+ boolean shouldSleep = true;
+ if (mResumedActivity != null) {
+ // Still have something resumed; can't sleep until it is paused.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
+ startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
+ "sleep");
+ shouldSleep = false;
+ } else if (mPausingActivity != null) {
+ // Still waiting for something to pause; can't sleep yet.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
+ shouldSleep = false;
+ }
+
+ if (!shuttingDown) {
+ if (containsStoppingActivity()) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
+ mTaskSupervisor.mStoppingActivities.size());
+
+ mTaskSupervisor.scheduleIdle();
+ shouldSleep = false;
+ }
+ }
+
+ if (shouldSleep) {
+ updateActivityVisibilities(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS, true /* notifyClients */);
+ }
+
+ return shouldSleep;
+ }
+
+ private boolean containsStoppingActivity() {
+ for (int i = mTaskSupervisor.mStoppingActivities.size() - 1; i >= 0; --i) {
+ ActivityRecord r = mTaskSupervisor.mStoppingActivities.get(i);
+ if (r.getTaskFragment() == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the TaskFragment is translucent and can have other contents visible behind
+ * it if needed. A TaskFragment is considered translucent if it don't contain a visible or
+ * starting (about to be visible) activity that is fullscreen (opaque).
+ * @param starting The currently starting activity or null if there is none.
+ */
+ @VisibleForTesting
+ boolean isTranslucent(ActivityRecord starting) {
+ if (!isAttached() || isForceHidden()) {
+ return true;
+ }
+ final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
+ PooledLambda.__(ActivityRecord.class), starting);
+ final ActivityRecord opaque = getActivity(p);
+ p.recycle();
+ return opaque == null;
+ }
+
+ private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
+ if (r.finishing) {
+ // We don't factor in finishing activities when determining translucency since
+ // they will be gone soon.
+ return false;
+ }
+
+ if (!r.visibleIgnoringKeyguard && r != starting) {
+ // Also ignore invisible activities that are not the currently starting
+ // activity (about to be visible).
+ return false;
+ }
+
+ if (r.occludesParent()) {
+ // Root task isn't translucent if it has at least one fullscreen activity
+ // that is visible.
+ return true;
+ }
+ return false;
+ }
+
+ ActivityRecord topRunningActivity() {
+ return topRunningActivity(false /* focusableOnly */);
+ }
+
+ ActivityRecord topRunningActivity(boolean focusableOnly) {
+ // Split into 2 to avoid object creation due to variable capture.
+ if (focusableOnly) {
+ return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+ } else {
+ return getActivity(ActivityRecord::canBeTopRunning);
+ }
+ }
+
+ boolean isTopActivityFocusable() {
+ final ActivityRecord r = topRunningActivity();
+ return r != null ? r.isFocusable()
+ : (isFocusable() && getWindowConfiguration().canReceiveKeys());
+ }
+
+ /**
+ * Returns the visibility state of this TaskFragment.
+ *
+ * @param starting The currently starting activity or null if there is none.
+ */
+ @TaskFragmentVisibility
+ int getVisibility(ActivityRecord starting) {
+ if (!isAttached() || isForceHidden()) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ if (isTopActivityLaunchedBehind()) {
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
+ boolean gotRootSplitScreenFragment = false;
+ boolean gotOpaqueSplitScreenPrimary = false;
+ boolean gotOpaqueSplitScreenSecondary = false;
+ boolean gotTranslucentFullscreen = false;
+ boolean gotTranslucentSplitScreenPrimary = false;
+ boolean gotTranslucentSplitScreenSecondary = false;
+ boolean shouldBeVisible = true;
+
+ // This TaskFragment is only considered visible if all its parent TaskFragments are
+ // considered visible, so check the visibility of all ancestor TaskFragment first.
+ final WindowContainer parent = getParent();
+ if (parent.asTaskFragment() != null) {
+ final int parentVisibility = parent.asTaskFragment().getVisibility(starting);
+ if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
+ // Can't be visible if parent isn't visible
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (parentVisibility == TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
+ // Parent is behind a translucent container so the highest visibility this container
+ // can get is that.
+ gotTranslucentFullscreen = true;
+ }
+ }
+
+ final List<TaskFragment> adjacentTaskFragments = new ArrayList<>();
+ final int windowingMode = getWindowingMode();
+ final boolean isAssistantType = isActivityTypeAssistant();
+ for (int i = parent.getChildCount() - 1; i >= 0; --i) {
+ final WindowContainer other = parent.getChildAt(i);
+ if (other == null) continue;
+
+ final boolean hasRunningActivities = hasRunningActivity(other);
+ if (other == this) {
+ // Should be visible if there is no other fragment occluding it, unless it doesn't
+ // have any running activities, not starting one and not home stack.
+ shouldBeVisible = hasRunningActivities
+ || (starting != null && starting.isDescendantOf(this))
+ || isActivityTypeHome();
+ break;
+ }
+
+ if (!hasRunningActivities) {
+ continue;
+ }
+
+ final int otherWindowingMode = other.getWindowingMode();
+ if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (isTranslucent(other, starting)) {
+ // Can be visible behind a translucent fullscreen TaskFragment.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+ && other.matchParentBounds()) {
+ if (isTranslucent(other, starting)) {
+ // Can be visible behind a translucent TaskFragment.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ // Multi-window TaskFragment that matches parent bounds would occlude other children
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && !gotOpaqueSplitScreenPrimary) {
+ gotRootSplitScreenFragment = true;
+ gotTranslucentSplitScreenPrimary = isTranslucent(other, starting);
+ gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && gotOpaqueSplitScreenPrimary) {
+ // Can't be visible behind another opaque TaskFragment in split-screen-primary.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && !gotOpaqueSplitScreenSecondary) {
+ gotRootSplitScreenFragment = true;
+ gotTranslucentSplitScreenSecondary = isTranslucent(other, starting);
+ gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && gotOpaqueSplitScreenSecondary) {
+ // Can't be visible behind another opaque TaskFragment in split-screen-secondary
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ }
+ if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible if we are in split-screen windowing mode and both halves of
+ // the screen are opaque.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ if (isAssistantType && gotRootSplitScreenFragment) {
+ // Assistant TaskFragment can't be visible behind split-screen. In addition to
+ // this not making sense, it also works around an issue here we boost the z-order
+ // of the assistant window surfaces in window manager whenever it is visible.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ final TaskFragment otherTaskFrag = other.asTaskFragment();
+ if (otherTaskFrag != null && otherTaskFrag.mAdjacentTaskFragment != null) {
+ if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
+ if (otherTaskFrag.isTranslucent(starting)
+ || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
+ // Can be visible behind a translucent adjacent TaskFragments.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ // Can not be visible behind adjacent TaskFragments.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else {
+ adjacentTaskFragments.add(otherTaskFrag);
+ }
+ }
+
+ }
+
+ if (!shouldBeVisible) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ // Handle cases when there can be a translucent split-screen TaskFragment on top.
+ switch (windowingMode) {
+ case WINDOWING_MODE_FULLSCREEN:
+ if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
+ // At least one of the split-screen TaskFragment that covers this one is
+ // translucent.
+ // When in split mode, home will be reparented to the secondary split while
+ // leaving TaskFragments not supporting split below. Due to
+ // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
+ // the bottom, this makes sure TaskFragments not in split roots won't occlude
+ // home task unexpectedly.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+ if (gotTranslucentSplitScreenPrimary) {
+ // Covered by translucent primary split-screen on top.
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ }
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+ if (gotTranslucentSplitScreenSecondary) {
+ // Covered by translucent secondary split-screen on top.
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ }
+ break;
+ }
+
+ // Lastly - check if there is a translucent fullscreen TaskFragment on top.
+ return gotTranslucentFullscreen
+ ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
+ : TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
+ private static boolean hasRunningActivity(WindowContainer wc) {
+ if (wc.asTaskFragment() != null) {
+ return wc.asTaskFragment().topRunningActivity() != null;
+ }
+ return wc.asActivityRecord() != null && !wc.asActivityRecord().finishing;
+ }
+
+ private static boolean isTranslucent(WindowContainer wc, ActivityRecord starting) {
+ if (wc.asTaskFragment() != null) {
+ return wc.asTaskFragment().isTranslucent(starting);
+ } else if (wc.asActivityRecord() != null) {
+ return !wc.asActivityRecord().occludesParent();
+ }
+ return false;
+ }
+
+
+ private boolean isTopActivityLaunchedBehind() {
+ final ActivityRecord top = topRunningActivity();
+ return top != null && top.mLaunchTaskBehind;
+ }
+
+ final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
+ boolean preserveWindows, boolean notifyClients) {
+ mTaskSupervisor.beginActivityVisibilityUpdate();
+ try {
+ mEnsureActivitiesVisibleHelper.process(
+ starting, configChanges, preserveWindows, notifyClients);
+ } finally {
+ mTaskSupervisor.endActivityVisibilityUpdate();
+ }
+ }
+
+ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
+ boolean deferPause) {
+ ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+ if (next == null || !next.canResumeByCompat()) {
+ return false;
+ }
+
+ next.delayedResume = false;
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
+ // we still want to check if the visibility of other windows have changed (e.g. bringing
+ // a fullscreen window forward to cover another freeform activity.)
+ if (taskDisplayArea.inMultiWindowMode()) {
+ taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */, true /* notifyClients */);
+ }
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+ + "resumed %s", next);
+ return false;
+ }
+
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ ProtoLog.v(WM_DEBUG_STATES,
+ "resumeTopActivity: Skip resume: some activity pausing.");
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top activity is paused,
+ // well that is the state we want.
+ if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and"
+ + " all paused");
+ return false;
+ }
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.mUserId + " is stopped");
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mTaskSupervisor.mStoppingActivities.remove(next);
+
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+
+ mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+
+ ActivityRecord lastResumed = null;
+ final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
+ if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) {
+ // So, why aren't we using prev here??? See the param comment on the method. prev
+ // doesn't represent the last resumed activity. However, the last focus stack does if
+ // it isn't null.
+ lastResumed = lastFocusedRootTask.getTopResumedActivity();
+ }
+
+ boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
+ if (mResumedActivity != null) {
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
+ pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
+ next, "resumeTopActivity");
+ }
+ if (pausing) {
+ ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"
+ + " start pausing");
+ // At this point we want to put the upcoming activity's process
+ // at the top of the LRU list, since we know we will be needing it
+ // very soon and it would be a waste to let it get killed if it
+ // happens to be sitting towards the end.
+ if (next.attachedToProcess()) {
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, false /* updateOomAdj */,
+ false /* addPendingTopUid */);
+ } else if (!next.isProcessRunning()) {
+ // Since the start-process is asynchronous, if we already know the process of next
+ // activity isn't running, we can start the process earlier to save the time to wait
+ // for the current activity to be paused.
+ final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
+ mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+ isTop ? "pre-top-activity" : "pre-activity");
+ }
+ if (lastResumed != null) {
+ lastResumed.setWillCloseOrEnterPip(true);
+ }
+ return true;
+ } else if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // It is possible for the activity to be resumed when we paused back stacks above if the
+ // next activity doesn't have to wait for pause to complete.
+ // So, nothing else to-do except:
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed "
+ + "(dontWaitForPause) %s", next);
+ return true;
+ }
+
+ // If the most recent activity was noHistory but was only stopped rather
+ // than stopped+finished because the device went to sleep, we need to make
+ // sure to finish it as we're making a new activity topmost.
+ if (shouldSleepActivities()) {
+ mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
+ }
+
+ if (prev != null && prev != next && next.nowVisible) {
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ prev.setVisibility(false);
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+ } else {
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+ }
+ }
+
+ // Launching this app's activity, make sure the app is no longer
+ // considered stopped.
+ try {
+ mTaskSupervisor.getActivityMetricsLogger()
+ .notifyBeforePackageUnstopped(next.packageName);
+ mAtmService.getPackageManager().setPackageStoppedState(
+ next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
+ } catch (RemoteException e1) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + next.packageName + ": " + e);
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ boolean anim = true;
+ final DisplayContent dc = taskDisplayArea.mDisplayContent;
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
+ }
+ if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_CLOSE);
+ }
+ prev.setVisibility(false);
+ } else {
+ if (DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
+ }
+ if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN,
+ next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
+ }
+ }
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
+ if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN);
+ }
+ }
+
+ if (anim) {
+ next.applyOptionsAnimation();
+ } else {
+ next.abortAndClearOptionsAnimation();
+ }
+
+ mTaskSupervisor.mNoAnimActivities.clear();
+
+ if (next.attachedToProcess()) {
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped
+ + " visibleRequested=" + next.mVisibleRequested);
+ }
+
+ // If the previous activity is translucent, force a visibility update of
+ // the next activity, so that it's added to WM's opening app list, and
+ // transition animation can be set up properly.
+ // For example, pressing Home button with a translucent activity in focus.
+ // Launcher is already visible in this case. If we don't add it to opening
+ // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
+ // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
+ final boolean lastActivityTranslucent = inMultiWindowMode()
+ || mLastPausedActivity != null && !mLastPausedActivity.occludesParent();
+
+ // This activity is now becoming visible.
+ if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+ next.setVisibility(true);
+ }
+
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
+
+ ActivityRecord lastResumedActivity =
+ lastFocusedRootTask == null ? null
+ : lastFocusedRootTask.getTopResumedActivity();
+ final ActivityRecord.State lastState = next.getState();
+
+ mAtmService.updateCpuStats();
+
+ ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
+
+ next.setState(RESUMED, "resumeTopActivity");
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
+
+ // Activity should also be visible if set mLaunchTaskBehind to true (see
+ // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
+ if (shouldBeVisible(next)) {
+ // We have special rotation behavior when here is some active activity that
+ // requests specific orientation or Keyguard is locked. Make sure all activity
+ // visibilities are set correctly as well as the transition is updated if needed
+ // to get the correct rotation behavior. Otherwise the following call to update
+ // the orientation may cause incorrect configurations delivered to client as a
+ // result of invisible window resize.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivity();
+ ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
+ + "%s, new next: %s", next, nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mTaskSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.mVisibleRequested || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ return true;
+ }
+
+ try {
+ final ClientTransaction transaction =
+ ClientTransaction.obtain(next.app.getThread(), next.appToken);
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int size = a.size();
+ if (!next.finishing && size > 0) {
+ if (DEBUG_RESULTS) {
+ Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
+ }
+ transaction.addCallback(ActivityResultItem.obtain(a));
+ }
+ }
+
+ if (next.newIntents != null) {
+ transaction.addCallback(
+ NewIntentItem.obtain(next.newIntents, true /* resume */));
+ }
+
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
+
+ EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
+ next.getTask().mTaskId, next.shortComponentName);
+
+ mAtmService.getAppWarningsLocked().onResumeActivity(next);
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+ next.abortAndClearOptionsAnimation();
+ transaction.setLifecycleStateRequest(
+ ResumeActivityItem.obtain(next.app.getReportedProcState(),
+ dc.isNextTransitionForward()));
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
+ + "%s", lastState, next);
+ next.setState(lastState, "resumeTopActivityInnerLocked");
+
+ // lastResumedActivity being non-null implies there is a lastStack present.
+ if (lastResumedActivity != null) {
+ lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
+ }
+
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
+ && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
+ next.showStartingWindow(false /* taskSwitch */);
+ }
+ mTaskSupervisor.startSpecificActivity(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.completeResumeLocked();
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ next.finishIfPossible("resume-exception", true /* oomAdj */);
+ return true;
+ }
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ next.showStartingWindow(false /* taskSwich */);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+ }
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
+ mTaskSupervisor.startSpecificActivity(next, true, true);
+ }
+
+ return true;
+ }
+
+ boolean shouldSleepOrShutDownActivities() {
+ return shouldSleepActivities() || mAtmService.mShuttingDown;
+ }
+
+ /**
+ * Returns true if the TaskFragment should be visible.
+ *
+ * @param starting The currently starting activity or null if there is none.
+ */
+ boolean shouldBeVisible(ActivityRecord starting) {
+ return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) {
+ return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
+ }
+
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming The activity we are currently trying to resume or null if this is not being
+ * called as part of resuming the top activity, so we shouldn't try to instigate
+ * a resume here if not null.
+ * @param reason The reason of pausing the activity.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
+ String reason) {
+ if (!hasDirectChildActivities()) {
+ return false;
+ }
+
+ ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
+ mResumedActivity);
+
+ if (mPausingActivity != null) {
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ + " state=" + mPausingActivity.getState());
+ if (!shouldSleepActivities()) {
+ // Avoid recursion among check for sleep and complete pause during sleeping.
+ // Because activity will be paused immediately after resume, just let pause
+ // be completed by the order of activity paused from clients.
+ completePause(false, resuming);
+ }
+ }
+ ActivityRecord prev = mResumedActivity;
+
+ if (prev == null) {
+ if (resuming == null) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ return false;
+ }
+
+ if (prev == resuming) {
+ Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+ return false;
+ }
+
+ ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
+ mTaskSupervisor.mNoHistoryActivities.add(prev);
+ }
+ prev.setState(PAUSING, "startPausingLocked");
+ prev.getTask().touchActiveTime();
+
+ mAtmService.updateCpuStats();
+
+ boolean pauseImmediately = false;
+ boolean shouldAutoPip = false;
+ if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
+ // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
+ // activity to be paused, while at the same time resuming the new resume activity
+ // only if the previous activity can't go into Pip since we want to give Pip
+ // activities a chance to enter Pip before resuming the next activity.
+ final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
+ "shouldResumeWhilePausing", userLeaving);
+ if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
+ shouldAutoPip = true;
+ } else if (!lastResumedCanPip) {
+ pauseImmediately = true;
+ } else {
+ // The previous activity may still enter PIP even though it did not allow auto-PIP.
+ }
+ }
+
+ boolean didAutoPip = false;
+ if (prev.attachedToProcess()) {
+ if (shouldAutoPip) {
+ ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+ + "directly: %s", prev);
+
+ didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
+ mPausingActivity = null;
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
+ try {
+ EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+ prev.shortComponentName, "userLeaving=" + userLeaving, reason);
+
+ mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+
+ // If we are not going to sleep, we want to ensure the device is
+ // awake until the next activity is started.
+ if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
+ mTaskSupervisor.acquireLaunchWakelock();
+ }
+
+ // If already entered PIP mode, no need to keep pausing.
+ if (mPausingActivity != null && !didAutoPip) {
+ // Have the window manager pause its key dispatching until the new
+ // activity has started. If we're pausing the activity just because
+ // the screen is being turned off and the UI is sleeping, don't interrupt
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
+ }
+
+ if (pauseImmediately) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePause(false, resuming);
+ return false;
+
+ } else {
+ prev.schedulePauseTimeout();
+ return true;
+ }
+
+ } else {
+ // This activity either failed to schedule the pause or it entered PIP mode,
+ // so just treat it as being paused now.
+ ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
+ if (resuming == null) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ return false;
+ }
+ }
+
+ @VisibleForTesting
+ void completePause(boolean resumeNext, ActivityRecord resuming) {
+ // Complete the pausing process of a pausing activity, so it doesn't make sense to
+ // operate on non-leaf tasks.
+ // warnForNonLeafTask("completePauseLocked");
+
+ ActivityRecord prev = mPausingActivity;
+ ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
+
+ if (prev != null) {
+ prev.setWillCloseOrEnterPip(false);
+ final boolean wasStopping = prev.isState(STOPPING);
+ prev.setState(PAUSED, "completePausedLocked");
+ if (prev.finishing) {
+ // We will update the activity visibility later, no need to do in
+ // completeFinishing(). Updating visibility here might also making the next
+ // activities to be resumed, and could result in wrong app transition due to
+ // lack of previous activity information.
+ ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
+ prev = prev.completeFinishing(false /* updateVisibility */,
+ "completePausedLocked");
+ } else if (prev.hasProcess()) {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+ + "wasStopping=%b visibleRequested=%b", prev, wasStopping,
+ prev.mVisibleRequested);
+ if (prev.deferRelaunchUntilPaused) {
+ // Complete the deferred relaunch that was waiting for pause to complete.
+ ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
+ prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
+ } else if (wasStopping) {
+ // We are also stopping, the stop request must have gone soon after the pause.
+ // We can't clobber it, because the stop confirmation will not be handled.
+ // We don't need to schedule another stop, we only need to let it happen.
+ prev.setState(STOPPING, "completePausedLocked");
+ } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+ // Clear out any deferred client hide we might currently have.
+ prev.setDeferHidingClient(false);
+ // If we were visible then resumeTopActivities will release resources before
+ // stopping.
+ prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
+ "completePauseLocked");
+ }
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
+ prev = null;
+ }
+ // It is possible the activity was freezing the screen before it was paused.
+ // In that case go ahead and remove the freeze this activity has on the screen
+ // since it is no longer visible.
+ if (prev != null) {
+ prev.stopFreezingScreenLocked(true /*force*/);
+ }
+ mPausingActivity = null;
+ }
+
+ if (resumeNext) {
+ final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
+ null /* targetOptions */);
+ } else {
+ // checkReadyForSleep();
+ final ActivityRecord top =
+ topRootTask != null ? topRootTask.topRunningActivity() : null;
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run, do resume anyway to start
+ // something. Also if the top activity on the root task is not the just paused
+ // activity, we need to go ahead and resume it to ensure we complete an
+ // in-flight app switch.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+ }
+
+ mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+
+ // Notify when the task stack has changed, but only if visibilities changed (not just
+ // focus). Also if there is an active root pinned task - we always want to notify it about
+ // task stack changes, because its positioning may depend on it.
+ if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
+ || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
+ mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+ }
+ }
+
+ @Override
+ void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ super.forAllTaskFragments(callback, traverseTopToBottom);
+ callback.accept(this);
+ }
+
+ @Override
+ void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ boolean isLeafTaskFrag = true;
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+ if (isLeafTaskFrag) callback.accept(this);
+ }
+
+ @Override
+ boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+ boolean isLeafTaskFrag = true;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ if (child.forAllLeafTaskFragments(callback)) {
+ return true;
+ }
+ }
+ }
+ if (isLeafTaskFrag) {
+ return callback.apply(this);
+ }
+ return false;
+ }
+
+ void addChild(ActivityRecord r) {
+ addChild(r, POSITION_TOP);
+ }
+
+ @Override
+ void addChild(WindowContainer child, int index) {
+ boolean isAddingActivity = child.asActivityRecord() != null;
+ final Task task = isAddingActivity ? getTask() : null;
+
+ // If this task had any child before we added this one.
+ boolean taskHadChild = task != null && task.hasChild();
+ // getActivityType() looks at the top child, so we need to read the type before adding
+ // a new child in case the new child is on top and UNDEFINED.
+ final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+
+ super.addChild(child, index);
+
+ if (isAddingActivity && task != null) {
+ child.asActivityRecord().inHistory = true;
+ task.onDescendantActivityAdded(taskHadChild, activityType, child.asActivityRecord());
+ }
+ }
+
+ void executeAppTransition(ActivityOptions options) {
+ // No app transition applied to the task fragment.
+ }
+
+ boolean shouldSleepActivities() {
+ return false;
+ }
+
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+ super.resolveOverrideConfiguration(newParentConfig);
+
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+
+ // Resolve override windowing mode to fullscreen for home task (even on freeform
+ // display), or split-screen if in split-screen mode.
+ if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
+ ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ }
+
+ // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
+ // pinned windowing mode.
+ if (!supportsMultiWindow()) {
+ final int candidateWindowingMode =
+ windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
+ if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
+ && candidateWindowingMode != WINDOWING_MODE_PINNED) {
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ if (isLeafTaskFragment()) {
+ resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
+ }
+ computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ }
+
+ boolean supportsMultiWindow() {
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this task supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
+ if (!mAtmService.mSupportsMultiWindow) {
+ return false;
+ }
+ final Task task = getTask();
+ if (task == null) {
+ return false;
+ }
+ if (tda == null) {
+ Slog.w(TAG, "Can't find TaskDisplayArea to determine support for multi"
+ + " window. Task id=" + getTaskId() + " attached=" + isAttached());
+ return false;
+ }
+ if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) {
+ // Not support non-resizable in multi window.
+ return false;
+ }
+
+ return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
+ }
+
+ private int getTaskId() {
+ return getTask() != null ? getTask().mTaskId : INVALID_TASK_ID;
+ }
+
+ private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
+ Rect previousBounds) {
+
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+ }
+ // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+ // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+ getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Use empty bounds to indicate "fill parent".
+ outOverrideBounds.setEmpty();
+ // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
+ // the parent or display is smaller than the size, the content may be cropped.
+ return;
+ }
+
+ adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ computeFreeformBounds(outOverrideBounds, newParentConfig);
+ return;
+ }
+ }
+
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
+ private void computeFreeformBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
+ // by policy, make sure the window remains within parent somewhere
+ final float density =
+ ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
+ final Rect parentBounds =
+ new Rect(newParentConfig.windowConfiguration.getBounds());
+ final DisplayContent display = getDisplayContent();
+ if (display != null) {
+ // If a freeform window moves below system bar, there is no way to move it again
+ // by touch. Because its caption is covered by system bar. So we exclude them
+ // from root task bounds. and then caption will be shown inside stable area.
+ final Rect stableBounds = new Rect();
+ display.getStableRect(stableBounds);
+ parentBounds.intersect(stableBounds);
+ }
+
+ fitWithinBounds(outBounds, parentBounds,
+ (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
+ (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+
+ // Prevent to overlap caption with stable insets.
+ final int offsetTop = parentBounds.top - outBounds.top;
+ if (offsetTop > 0) {
+ outBounds.offset(0, offsetTop);
+ }
+ }
+
+ /**
+ * Adjusts bounds to stay within root task bounds.
+ *
+ * Since bounds might be outside of root task bounds, this method tries to move the bounds in
+ * a way that keep them unchanged, but be contained within the root task bounds.
+ *
+ * @param bounds Bounds to be adjusted.
+ * @param rootTaskBounds Bounds within which the other bounds should remain.
+ * @param overlapPxX The amount of px required to be visible in the X dimension.
+ * @param overlapPxY The amount of px required to be visible in the Y dimension.
+ */
+ private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
+ int overlapPxY) {
+ if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
+ return;
+ }
+
+ // For each side of the parent (eg. left), check if the opposing side of the window (eg.
+ // right) is at least overlap pixels away. If less, offset the window by that difference.
+ int horizontalDiff = 0;
+ // If window is smaller than overlap, use it's smallest dimension instead
+ int overlapLR = Math.min(overlapPxX, bounds.width());
+ if (bounds.right < (rootTaskBounds.left + overlapLR)) {
+ horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
+ } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
+ horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
+ }
+ int verticalDiff = 0;
+ int overlapTB = Math.min(overlapPxY, bounds.width());
+ if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
+ verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
+ } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
+ verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
+ }
+ bounds.offset(horizontalDiff, verticalDiff);
+ }
+
+ /**
+ * Ensures all visible activities at or below the input activity have the right configuration.
+ */
+ void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+ mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
+ }
+
+ void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
+ @NonNull Configuration parentConfig) {
+ int minWidth = mMinWidth;
+ int minHeight = mMinHeight;
+ // If the task has no requested minimal size, we'd like to enforce a minimal size
+ // so that the user can not render the task fragment too small to manipulate. We don't need
+ // to do this for the root pinned task as the bounds are controlled by the system.
+ if (!inPinnedWindowingMode()) {
+ final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
+ final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
+ if (minWidth == INVALID_MIN_SIZE) {
+ minWidth = defaultMinSize;
+ }
+ if (minHeight == INVALID_MIN_SIZE) {
+ minHeight = defaultMinSize;
+ }
+ }
+ if (bounds.isEmpty()) {
+ // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
+ // do, we can just skip.
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
+ return;
+ }
+ bounds.set(parentBounds);
+ }
+ final boolean adjustWidth = minWidth > bounds.width();
+ final boolean adjustHeight = minHeight > bounds.height();
+ if (!(adjustWidth || adjustHeight)) {
+ return;
+ }
+
+ if (adjustWidth) {
+ if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
+ bounds.left = bounds.right - minWidth;
+ } else {
+ // Either left bounds match, or neither match, or the previous bounds were
+ // fullscreen and we default to keeping left.
+ bounds.right = bounds.left + minWidth;
+ }
+ }
+ if (adjustHeight) {
+ if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
+ bounds.top = bounds.bottom - minHeight;
+ } else {
+ // Either top bounds match, or neither match, or the previous bounds were
+ // fullscreen and we default to keeping top.
+ bounds.bottom = bounds.top + minHeight;
+ }
+ }
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig) {
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+ null /* compatInsets */);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
+ if (overrideDisplayInfo != null) {
+ // Make sure the screen related configs can be computed by the provided display info.
+ inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
+ null /* compatInsets */);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig,
+ @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ if (compatInsets != null) {
+ // Make sure the app bounds can be computed by the compat insets.
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+ compatInsets);
+ }
+
+ /**
+ * Forces the app bounds related configuration can be computed by
+ * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+ * ActivityRecord.CompatDisplayInsets)}.
+ */
+ private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+ final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.setEmpty();
+ }
+ inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ }
+
+ /**
+ * Calculates configuration values used by the client to get resources. This should be run
+ * using app-facing bounds (bounds unmodified by animations or transient interactions).
+ *
+ * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
+ * configuring an "inherit-bounds" window which means that all configuration settings would
+ * just be inherited from the parent configuration.
+ **/
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
+ @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ }
+
+ float density = inOutConfig.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = parentConfig.densityDpi;
+ }
+ density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+
+ // The bounds may have been overridden at this level. If the parent cannot cover these
+ // bounds, the configuration is still computed according to the override bounds.
+ final boolean insideParentBounds;
+
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
+ if (resolvedBounds == null || resolvedBounds.isEmpty()) {
+ mTmpFullBounds.set(parentBounds);
+ insideParentBounds = true;
+ } else {
+ mTmpFullBounds.set(resolvedBounds);
+ insideParentBounds = parentBounds.contains(resolvedBounds);
+ }
+
+ // Non-null compatibility insets means the activity prefers to keep its original size, so
+ // out bounds doesn't need to be restricted by the parent or current display
+ final boolean customContainerPolicy = compatInsets != null;
+
+ Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (outAppBounds == null || outAppBounds.isEmpty()) {
+ // App-bounds hasn't been overridden, so calculate a value for it.
+ inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
+ outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+
+ if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
+ final Rect containingAppBounds;
+ if (insideParentBounds) {
+ containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
+ } else {
+ // Restrict appBounds to display non-decor rather than parent because the
+ // override bounds are beyond the parent. Otherwise, it won't match the
+ // overridden bounds.
+ final TaskDisplayArea displayArea = getDisplayArea();
+ containingAppBounds = displayArea != null
+ ? displayArea.getWindowConfiguration().getAppBounds() : null;
+ }
+ if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
+ outAppBounds.intersect(containingAppBounds);
+ }
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
+ || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
+ mTmpNonDecorBounds.set(mTmpFullBounds);
+ mTmpStableBounds.set(mTmpFullBounds);
+ } else if (!customContainerPolicy
+ && (overrideDisplayInfo != null || getDisplayContent() != null)) {
+ final DisplayInfo di = overrideDisplayInfo != null
+ ? overrideDisplayInfo
+ : getDisplayContent().getDisplayInfo();
+
+ // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
+ // area, i.e. the screen area without the system bars.
+ // The non decor inset are areas that could never be removed in Honeycomb. See
+ // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
+ calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
+ } else {
+ // Apply the given non-decor and stable insets to calculate the corresponding bounds
+ // for screen size of configuration.
+ int rotation = inOutConfig.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED) {
+ rotation = parentConfig.windowConfiguration.getRotation();
+ }
+ if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
+ mTmpNonDecorBounds.set(mTmpFullBounds);
+ mTmpStableBounds.set(mTmpFullBounds);
+ compatInsets.getBoundsByRotation(mTmpBounds, rotation);
+ intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
+ compatInsets.mNonDecorInsets[rotation]);
+ intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
+ compatInsets.mStableInsets[rotation]);
+ outAppBounds.set(mTmpNonDecorBounds);
+ } else {
+ // Set to app bounds because it excludes decor insets.
+ mTmpNonDecorBounds.set(outAppBounds);
+ mTmpStableBounds.set(outAppBounds);
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
+ inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
+ ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
+ : overrideScreenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
+ inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
+ ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
+ : overrideScreenHeightDp;
+ }
+
+ if (inOutConfig.smallestScreenWidthDp
+ == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ if (WindowConfiguration.isFloating(windowingMode)) {
+ // For floating tasks, calculate the smallest width from the bounds of the task
+ inOutConfig.smallestScreenWidthDp = (int) (
+ Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
+ }
+ // otherwise, it will just inherit
+ }
+ }
+
+ if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+ inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ }
+ if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+ // For calculating screen layout, we need to use the non-decor inset screen area for the
+ // calculation for compatibility reasons, i.e. screen area without system bars that
+ // could never go away in Honeycomb.
+ int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+ int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
+ // undefined so it can't be used.
+ if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ compatScreenWidthDp = inOutConfig.screenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ compatScreenHeightDp = inOutConfig.screenHeightDp;
+ }
+ // Reducing the screen layout starting from its parent config.
+ inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
+ compatScreenWidthDp, compatScreenHeightDp);
+ }
+ }
+
+ /**
+ * Gets bounds with non-decor and stable insets applied respectively.
+ *
+ * If bounds overhangs the display, those edges will not get insets. See
+ * {@link #intersectWithInsetsIfFits}
+ *
+ * @param outNonDecorBounds where to place bounds with non-decor insets applied.
+ * @param outStableBounds where to place bounds with stable insets applied.
+ * @param bounds the bounds to inset.
+ */
+ void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
+ DisplayInfo displayInfo) {
+ outNonDecorBounds.set(bounds);
+ outStableBounds.set(bounds);
+ final Task rootTask = getRootTaskFragment().asTask();
+ if (rootTask == null || rootTask.mDisplayContent == null) {
+ return;
+ }
+ mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+
+ policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
+ }
+
+ /**
+ * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
+ * intersectBounds on a side, then the respective side will not be intersected.
+ *
+ * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
+ * inset on that side is no-longer applicable. This scenario happens when a task's minimal
+ * bounds are larger than the provided parent/display bounds.
+ *
+ * @param inOutBounds the bounds to intersect.
+ * @param intersectBounds the bounds to intersect with.
+ * @param intersectInsets insets to apply to intersectBounds before intersecting.
+ */
+ static void intersectWithInsetsIfFits(
+ Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
+ if (inOutBounds.right <= intersectBounds.right) {
+ inOutBounds.right =
+ Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
+ }
+ if (inOutBounds.bottom <= intersectBounds.bottom) {
+ inOutBounds.bottom =
+ Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
+ }
+ if (inOutBounds.left >= intersectBounds.left) {
+ inOutBounds.left =
+ Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
+ }
+ if (inOutBounds.top >= intersectBounds.top) {
+ inOutBounds.top =
+ Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
+ }
+ }
+
+ /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
+ static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
+ int screenHeightDp) {
+ sourceScreenLayout = sourceScreenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+ final int longSize = Math.max(screenWidthDp, screenHeightDp);
+ final int shortSize = Math.min(screenWidthDp, screenHeightDp);
+ return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
+ }
+
+ @Override
+ public int getActivityType() {
+ final int applicationType = super.getActivityType();
+ if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
+ return applicationType;
+ }
+ return getTopChild().getActivityType();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ super.onConfigurationChanged(newParentConfig);
+
+ if (mTaskFragmentOrganizer != null) {
+ // Parent config may have changed. The controller will check if there is any important
+ // config change for the organizer.
+ mTaskFragmentOrganizerController
+ .onTaskFragmentParentInfoChanged(mTaskFragmentOrganizer, this);
+ mTaskFragmentOrganizerController
+ .onTaskFragmentInfoChanged(mTaskFragmentOrganizer, this);
+ }
+ }
+
+ // TODO(b/190433129) call when TaskFragment is created from WCT#createTaskFragment
+ private void sendTaskFragmentAppeared() {
+ if (mTaskFragmentOrganizer != null) {
+ mTaskFragmentOrganizerController.onTaskFragmentAppeared(mTaskFragmentOrganizer, this);
+ }
+ }
+
+ private void sendTaskFragmentVanished() {
+ if (mTaskFragmentOrganizer != null) {
+ mTaskFragmentOrganizerController.onTaskFragmentVanished(mTaskFragmentOrganizer, this);
+ }
+ }
+
+ /**
+ * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be
+ * called from {@link Task}.
+ */
+ TaskFragmentInfo getTaskFragmentInfo() {
+ List<IBinder> childActivities = new ArrayList<>();
+ for (int i = 0; i < getChildCount(); i++) {
+ WindowContainer wc = getChildAt(i);
+ if (mTaskFragmentOrganizerPid != ActivityRecord.INVALID_PID
+ && wc.asActivityRecord() != null
+ && wc.asActivityRecord().getPid() == mTaskFragmentOrganizerPid) {
+ // Only includes Activities that belong to the organizer process for security.
+ childActivities.add(wc.asActivityRecord().appToken);
+ }
+ }
+ return new TaskFragmentInfo(
+ mFragmentToken,
+ mRemoteToken.toWindowContainerToken(),
+ getConfiguration(),
+ getChildCount() == 0,
+ isVisible(),
+ childActivities);
+ }
+
+ @Nullable
+ IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ ITaskFragmentOrganizer getTaskFragmentOrganizer() {
+ return mTaskFragmentOrganizer;
+ }
+
+ /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
+ void clearLastPausedActivity() {
+ forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
+ }
+
+ /**
+ * Sets {@link #mMinWidth} and {@link #mMinWidth} to this TaskFragment.
+ * It is usually set from the parent {@link Task} when adding the TaskFragment to the window
+ * hierarchy.
+ */
+ void setMinDimensions(int minWidth, int minHeight) {
+ if (asTask() != null) {
+ throw new UnsupportedOperationException("This method must not be used to Task. The "
+ + " minimum dimension of Task should be passed from Task constructor.");
+ }
+ mMinWidth = minWidth;
+ mMinHeight = minHeight;
+ }
+
+ @Override
+ void removeImmediately() {
+ if (mRemoving) {
+ return;
+ }
+ mRemoving = true;
+ super.removeImmediately();
+ sendTaskFragmentVanished();
+ mRemoving = false;
+ }
+
+ boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
+ boolean printed = false;
+ Runnable headerPrinter = () -> {
+ if (needSep) {
+ pw.println();
+ }
+ if (header != null) {
+ header.run();
+ }
+
+ dumpInner(prefix, pw, dumpAll, dumpPackage);
+ };
+
+ if (dumpPackage == null) {
+ // If we are not filtering by package, we want to print absolutely everything,
+ // so always print the header even if there are no tasks/activities inside.
+ headerPrinter.run();
+ headerPrinter = null;
+ printed = true;
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ printed |= child.asTaskFragment().dump(prefix + " ", fd, pw, dumpAll,
+ dumpClient, dumpPackage, needSep, headerPrinter);
+ } else if (child.asActivityRecord() != null) {
+ ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + " ",
+ "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter,
+ getTask());
+ }
+ }
+
+ return printed;
+ }
+
+ void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ pw.print(prefix); pw.print("* "); pw.println(this);
+ pw.println(prefix + " mBounds=" + getRequestedOverrideBounds());
+ if (dumpAll) {
+ printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+ prefix + " mLastPausedActivity: ", null);
+ }
+ }
+
+ @Override
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ final ActivityRecord topActivity = topRunningActivity();
+ proto.write(USER_ID, topActivity != null ? topActivity.mUserId : USER_NULL);
+ proto.write(TITLE, topActivity != null ? topActivity.intent.getComponent()
+ .flattenToShortString() : "TaskFragment");
+ proto.end(token);
+ }
+
+ @Override
+ long getProtoFieldId() {
+ return TASK_FRAGMENT;
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+
+ super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+
+ proto.write(DISPLAY_ID, getDisplayId());
+ proto.write(ACTIVITY_TYPE, getActivityType());
+ proto.write(MIN_WIDTH, mMinWidth);
+ proto.write(MIN_HEIGHT, mMinHeight);
+
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
new file mode 100644
index 000000000000..56d29dee7b45
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -0,0 +1,234 @@
+/*
+ * 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.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
+
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.ITaskFragmentOrganizerController;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Stores and manages the client {@link android.window.TaskFragmentOrganizer}.
+ */
+public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub {
+ private static final String TAG = "TaskFragmentOrganizerController";
+
+ private final ActivityTaskManagerService mAtmService;
+ private final WindowManagerGlobalLock mGlobalLock;
+ private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
+ new WeakHashMap<>();
+ private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
+ new WeakHashMap<>();
+ /**
+ * A Map which manages the relationship between
+ * {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
+ */
+ private final ArrayMap<IBinder, TaskFragmentController> mTaskFragmentOrganizerControllers =
+ new ArrayMap<>();
+
+ TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
+ mAtmService = atm;
+ mGlobalLock = atm.mGlobalLock;
+ }
+
+ /**
+ * A class to manage {@link ITaskFragmentOrganizer} and its organized
+ * {@link TaskFragment TaskFragments}.
+ */
+ private class TaskFragmentController implements IBinder.DeathRecipient {
+ private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
+ private final ITaskFragmentOrganizer mOrganizer;
+
+ TaskFragmentController(ITaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ try {
+ mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mGlobalLock) {
+ removeOrganizer(mOrganizer);
+ }
+ }
+
+ void addTaskFragment(TaskFragment taskFragment) {
+ if (!mOrganizedTaskFragments.contains(taskFragment)) {
+ mOrganizedTaskFragments.add(taskFragment);
+ }
+ }
+
+ void removeTaskFragment(TaskFragment taskFragment) {
+ mOrganizedTaskFragments.remove(taskFragment);
+ }
+
+ void dispose() {
+ mOrganizedTaskFragments.forEach(TaskFragment::removeImmediately);
+ mOrganizedTaskFragments.clear();
+ mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
+ }
+ }
+
+ @Override
+ public void registerOrganizer(ITaskFragmentOrganizer organizer) {
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Register task fragment organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ if (mTaskFragmentOrganizerControllers.containsKey(organizer.asBinder())) {
+ throw new IllegalStateException(
+ "Replacing existing organizer currently unsupported");
+ }
+ mTaskFragmentOrganizerControllers.put(organizer.asBinder(),
+ new TaskFragmentController(organizer));
+ }
+ }
+
+ @Override
+ public void unregisterOrganizer(ITaskFragmentOrganizer organizer) {
+ validateAndGetController(organizer);
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Unregister task fragment organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ removeOrganizer(organizer);
+ }
+ }
+
+ void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ final TaskFragmentController controller = validateAndGetController(organizer);
+
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
+ final TaskFragmentInfo info = tf.getTaskFragmentInfo();
+ final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
+ "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
+ controller.addTaskFragment(tf);
+ try {
+ organizer.onTaskFragmentAppeared(
+ new TaskFragmentAppearedInfo(info, outSurfaceControl));
+ mLastSentTaskFragmentInfos.put(tf, info);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentAppeared callback", e);
+ }
+ }
+
+ void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ validateAndGetController(organizer);
+
+ // Check if the info is different from the last reported info.
+ final TaskFragmentInfo info = tf.getTaskFragmentInfo();
+ final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
+ if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
+ info.getConfiguration(), lastInfo.getConfiguration())) {
+ return;
+ }
+
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s", tf.getName());
+ try {
+ organizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo());
+ mLastSentTaskFragmentInfos.put(tf, info);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
+ }
+ }
+
+ void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ final TaskFragmentController controller = validateAndGetController(organizer);
+
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
+ try {
+ organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentVanished callback", e);
+ }
+ mLastSentTaskFragmentInfos.remove(tf);
+ mLastSentTaskFragmentParentConfigs.remove(tf);
+ controller.removeTaskFragment(tf);
+ }
+
+ void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ validateAndGetController(organizer);
+
+ // Check if the parent info is different from the last reported parent info.
+ if (tf.getParent() == null || tf.getParent().asTask() == null) {
+ mLastSentTaskFragmentParentConfigs.remove(tf);
+ return;
+ }
+ final Task parent = tf.getParent().asTask();
+ final Configuration parentConfig = parent.getConfiguration();
+ final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
+ if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)) {
+ return;
+ }
+
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "TaskFragment parent info changed name=%s parentTaskId=%d",
+ tf.getName(), parent.mTaskId);
+ try {
+ organizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
+ mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
+ }
+ }
+
+ private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+ final TaskFragmentController controller = validateAndGetController(organizer);
+ // remove all of the children of the organized TaskFragment
+ controller.dispose();
+ mTaskFragmentOrganizerControllers.remove(organizer.asBinder());
+ }
+
+ /**
+ * Makes sure that the organizer has been correctly registered to prevent any Sidecar
+ * implementation from organizing {@link TaskFragment} without registering first. In such case,
+ * we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
+ * {@link TaskFragment} after the organizer process died.
+ */
+ private TaskFragmentController validateAndGetController(ITaskFragmentOrganizer organizer) {
+ final TaskFragmentController controller =
+ mTaskFragmentOrganizerControllers.get(organizer.asBinder());
+ if (controller == null) {
+ throw new IllegalArgumentException(
+ "TaskFragmentOrganizer has not been registered. Organizer=" + organizer);
+ }
+ return controller;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 2dc63ce38111..a507abdae8a2 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,15 +23,13 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
-import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
-import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
+import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
import android.os.Binder;
@@ -69,13 +67,6 @@ import java.util.function.Consumer;
class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final String TAG = "TaskOrganizerController";
- /**
- * Masks specifying which configurations are important to report back to an organizer when
- * changed.
- */
- private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
- private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
-
// The set of modes that are currently supports
// TODO: Remove once the task organizer can support all modes
@VisibleForTesting
@@ -389,6 +380,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mOrganizer.mTaskOrganizer, t);
}
}
+ if (mService.getTransitionController().isShellTransitionsEnabled()) {
+ // dispose is only called outside of transitions (eg during unregister). Since
+ // we "migrate" surfaces when replacing organizers, visibility gets delegated
+ // to transitions; however, since there is no transition at this point, we have
+ // to manually show the surface here.
+ if (t.mTaskOrganizer != null && t.getSurfaceControl() != null) {
+ t.getSyncTransaction().show(t.getSurfaceControl());
+ }
+ }
}
// Remove organizer state after removing tasks so we get a chance to send
@@ -481,7 +481,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mGlobalLock) {
+ final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+ final Runnable withGlobalLock = () -> {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
organizer.asBinder(), uid);
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
@@ -490,10 +491,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
new TaskOrganizerState(organizer, uid));
}
- final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
mService.mRootWindowContainer.forAllTasks((task) -> {
- if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
+ if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES,
+ task.getWindowingMode())) {
return;
}
@@ -503,11 +504,19 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
if (returnTask) {
SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
"TaskOrganizerController.registerTaskOrganizer");
- taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+ taskInfos.add(
+ new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
}
});
- return new ParceledListSlice<>(taskInfos);
+ };
+ if (mService.getTransitionController().isShellTransitionsEnabled()) {
+ mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock);
+ } else {
+ synchronized (mGlobalLock) {
+ withGlobalLock.run();
+ }
}
+ return new ParceledListSlice<>(taskInfos);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -519,7 +528,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mGlobalLock) {
+ final Runnable withGlobalLock = () -> {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state == null) {
return;
@@ -528,6 +537,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
organizer.asBinder(), uid);
state.unlinkDeath();
state.dispose();
+ };
+ if (mService.getTransitionController().isShellTransitionsEnabled()) {
+ mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock);
+ } else {
+ synchronized (mGlobalLock) {
+ withGlobalLock.run();
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -766,18 +782,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mTmpTaskInfo.configuration.unset();
task.fillTaskInfo(mTmpTaskInfo);
- boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo);
- if (!changed) {
- int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
- final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
- ? (int) mTmpTaskInfo.configuration.windowConfiguration.diff(
- lastInfo.configuration.windowConfiguration,
- true /* compareUndefined */) : 0;
- if ((winCfgChanges & REPORT_WINDOW_CONFIGS) == 0) {
- cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
- }
- changed = (cfgChanges & REPORT_CONFIGS) != 0;
- }
+ boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo)
+ || !configurationsAreEqualForOrganizer(
+ mTmpTaskInfo.configuration, lastInfo.configuration);
if (!(changed || force)) {
// mTmpTaskInfo will be reused next time.
return;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e74371036619..c85615d258e9 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -118,6 +118,11 @@ class TaskSnapshotController {
*/
private final boolean mIsRunningOnWear;
+ /**
+ * Flag indicating if device configuration has disabled app snapshots.
+ */
+ private final boolean mConfigDisableTaskSnapshots;
+
TaskSnapshotController(WindowManagerService service) {
mService = service;
mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory);
@@ -131,6 +136,8 @@ class TaskSnapshotController {
PackageManager.FEATURE_WATCH);
mHighResTaskSnapshotScale = mService.mContext.getResources().getFloat(
com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+ mConfigDisableTaskSnapshots = mService.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableTaskSnapshots);
}
void systemReady() {
@@ -488,7 +495,8 @@ class TaskSnapshotController {
}
boolean shouldDisableSnapshots() {
- return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
+ return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT
+ || mConfigDisableTaskSnapshots;
}
/**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0cd098070401..1ac16664244f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -16,11 +16,13 @@
package com.android.server.wm;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -32,12 +34,16 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -124,8 +130,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
+ private TransitionInfo.AnimationOptions mOverrideOptions;
+
private @TransitionState int mState = STATE_COLLECTING;
- private boolean mReadyCalled = false;
+ private final ReadyTracker mReadyTracker = new ReadyTracker();
+
+ // TODO(b/188595497): remove when not needed.
+ /** @see RecentsAnimationController#mNavigationBarAttachedToApp */
+ private boolean mNavBarAttachedToApp = false;
+ private int mNavBarDisplayId = INVALID_DISPLAY;
Transition(@WindowManager.TransitionType int type, @WindowManager.TransitionFlags int flags,
TransitionController controller, BLASTSyncEngine syncEngine) {
@@ -136,6 +149,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mSyncId = mSyncEngine.startSyncSet(this);
}
+ void addFlag(int flag) {
+ mFlags |= flag;
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
@@ -153,9 +170,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mState = STATE_STARTED;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
mSyncId);
- if (mReadyCalled) {
- setReady();
- }
+ applyReady();
}
/**
@@ -170,6 +185,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
curr = curr.getParent()) {
mChanges.put(curr, new ChangeInfo(curr));
+ if (isReadyGroup(curr)) {
+ mReadyTracker.addGroup(curr);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
+ + " Transition %d with root=%s", mSyncId, curr);
+ }
}
if (mParticipants.contains(wc)) return;
mSyncEngine.addToSyncSet(mSyncId, wc);
@@ -207,25 +227,46 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
/**
+ * Set animation options for collecting transition by ActivityRecord.
+ * @param options AnimationOptions captured from ActivityOptions
+ */
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options) {
+ if (mSyncId < 0) return;
+ mOverrideOptions = options;
+ }
+
+ /**
* Call this when all known changes related to this transition have been applied. Until
* all participants have finished drawing, the transition can still collect participants.
*
* If this is called before the transition is started, it will be deferred until start.
+ *
+ * @param wc A reference point to determine which ready-group to update. For now, each display
+ * has its own ready-group, so this is used to look-up which display to mark ready.
+ * The transition will wait for all groups to be ready.
*/
- void setReady(boolean ready) {
+ void setReady(WindowContainer wc, boolean ready) {
if (mSyncId < 0) return;
- if (mState < STATE_STARTED) {
- mReadyCalled = ready;
- return;
- }
+ mReadyTracker.setReadyFrom(wc, ready);
+ applyReady();
+ }
+
+ private void applyReady() {
+ if (mState < STATE_STARTED) return;
+ final boolean ready = mReadyTracker.allReady();
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Set transition ready=%b %d", ready, mSyncId);
mSyncEngine.setReady(mSyncId, ready);
}
- /** @see #setReady . This calls with parameter true. */
- void setReady() {
- setReady(true);
+ /**
+ * Sets all possible ready groups to ready.
+ * @see ReadyTracker#setAllReady.
+ */
+ void setAllReady() {
+ if (mSyncId < 0) return;
+ mReadyTracker.setAllReady();
+ applyReady();
}
/**
@@ -275,12 +316,38 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
// Commit all going-invisible containers
+ boolean activitiesWentInvisible = false;
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar != null && !ar.isVisibleRequested()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- " Commit activity becoming invisible: %s", ar);
- ar.commitVisibility(false /* visible */, false /* performLayout */);
+ if (ar != null) {
+ if (!ar.isVisibleRequested()) {
+ boolean commitVisibility = true;
+ if (ar.getDeferHidingClient() && ar.getTask() != null) {
+ if (ar.pictureInPictureArgs != null
+ && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
+ mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs);
+ // Avoid commit visibility to false here, or else we will get a sudden
+ // "flash" / surface going invisible for a split second.
+ commitVisibility = false;
+ } else {
+ mController.mAtm.mTaskSupervisor.mUserLeaving = true;
+ ar.getTaskFragment().startPausing(false /* uiSleeping */,
+ null /* resuming */, "finishTransition");
+ mController.mAtm.mTaskSupervisor.mUserLeaving = false;
+ }
+ }
+ if (commitVisibility) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Commit activity becoming invisible: %s", ar);
+ ar.commitVisibility(false /* visible */, false /* performLayout */);
+ activitiesWentInvisible = true;
+ }
+ }
+ if (mChanges.get(ar).mVisible != ar.isVisibleRequested()) {
+ // Legacy dispatch relies on this (for now).
+ ar.mEnteringAnimation = ar.isVisibleRequested();
+ }
+ mController.dispatchLegacyAppTransitionFinished(ar);
}
final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
if (wt != null && !wt.isVisibleRequested()) {
@@ -289,6 +356,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
wt.commitVisibility(false /* visible */);
}
}
+ if (activitiesWentInvisible) {
+ // Always schedule stop processing when transition finishes because activities don't
+ // stop while they are in a transition thus their stop could still be pending.
+ mController.mAtm.mTaskSupervisor
+ .scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
+ }
+
+ legacyRestoreNavigationBarFromApp();
}
void abort() {
@@ -297,6 +372,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (mState != STATE_COLLECTING) {
throw new IllegalStateException("Too late to abort.");
}
+ mController.dispatchLegacyAppTransitionCancelled();
mState = STATE_ABORT;
// Syncengine abort will call through to onTransactionReady()
mSyncEngine.abort(mSyncId);
@@ -327,6 +403,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
.getPendingTransaction().merge(transaction);
mSyncId = -1;
+ mOverrideOptions = null;
return;
}
@@ -340,9 +417,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
+ info.setAnimationOptions(mOverrideOptions);
+
+ // TODO(b/188669821): Move to animation impl in shell.
+ handleLegacyRecentsStartBehavior(displayId, info);
handleNonAppWindowsInTransition(displayId, mType, mFlags);
+ reportStartReasonsToLogger();
+
// Manually show any activities that are visibleRequested. This is needed to properly
// support simultaneous animation queueing/merging. Specifically, if transition A makes
// an activity invisible, it's finishTransaction (which is applied *after* the animation)
@@ -360,6 +443,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
+ mController.dispatchLegacyAppTransitionStarting(info);
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
@@ -375,6 +459,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
cleanUpOnFailure();
}
mSyncId = -1;
+ mOverrideOptions = null;
}
/**
@@ -394,6 +479,100 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
finishTransition();
}
+ /** @see RecentsAnimationController#attachNavigationBarToApp */
+ private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) {
+ if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
+ return;
+ }
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
+ if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+ // Skip the case where the nav bar is controlled by fade rotation.
+ || dc.getFadeRotationAnimationController() != null) {
+ return;
+ }
+
+ WindowContainer topWC = null;
+ // Find the top-most non-home, closing app.
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId
+ || c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD
+ || !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) {
+ continue;
+ }
+ topWC = WindowContainer.fromBinder(c.getContainer().asBinder());
+ break;
+ }
+ if (topWC == null || topWC.inMultiWindowMode()) {
+ return;
+ }
+
+ final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+ if (navWindow == null || navWindow.mToken == null) {
+ return;
+ }
+ mNavBarAttachedToApp = true;
+ mNavBarDisplayId = displayId;
+ navWindow.mToken.cancelAnimation();
+ final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+ final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+ t.reparent(navSurfaceControl, topWC.getSurfaceControl());
+ t.show(navSurfaceControl);
+
+ final WindowContainer imeContainer = dc.getImeContainer();
+ if (imeContainer.isVisible()) {
+ t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
+ } else {
+ // Place the nav bar on top of anything else in the top activity.
+ t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
+ }
+ if (mController.mStatusBar != null) {
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false);
+ }
+ }
+
+ /** @see RecentsAnimationController#restoreNavigationBarFromApp */
+ void legacyRestoreNavigationBarFromApp() {
+ if (!mNavBarAttachedToApp) return;
+ mNavBarAttachedToApp = false;
+
+ if (mController.mStatusBar != null) {
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true);
+ }
+
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId);
+ final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+ if (navWindow == null) return;
+ navWindow.setSurfaceTranslationY(0);
+
+ final WindowToken navToken = navWindow.mToken;
+ if (navToken == null) return;
+ final SurfaceControl.Transaction t = dc.getPendingTransaction();
+ final WindowContainer parent = navToken.getParent();
+ t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
+
+ boolean animate = false;
+ // Search for the home task. If it is supposed to be visible, then the navbar is not at
+ // the bottom of the screen, so we need to animate it.
+ for (int i = 0; i < mTargets.size(); ++i) {
+ final Task task = mTargets.valueAt(i).asTask();
+ if (task == null || !task.isHomeOrRecentsRootTask()) continue;
+ animate = task.isVisibleRequested();
+ break;
+ }
+
+ if (animate) {
+ final NavBarFadeAnimationController controller =
+ new NavBarFadeAnimationController(dc);
+ controller.fadeWindowToken(true);
+ } else {
+ // Reparent the SurfaceControl of nav bar token back.
+ t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+ }
+ }
+
private void handleNonAppWindowsInTransition(int displayId,
@WindowManager.TransitionType int transit, int flags) {
final DisplayContent dc =
@@ -427,6 +606,23 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
+ private void reportStartReasonsToLogger() {
+ // Record transition start in metrics logger. We just assume everything is "DRAWN"
+ // at this point since splash-screen is a presentation (shell) detail.
+ ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
+ if (r == null) continue;
+ // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
+ // ready due to starting-window.
+ reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
+ && !r.mLastAllReadyAtSync)
+ ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+ }
+ mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+ reasons);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -629,6 +825,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (reportIfNotTop(wc)) {
tmpList.add(wc);
}
+ // Wallpaper must be the top (regardless of how nested it is in DisplayAreas).
+ boolean skipIntermediateReports = isWallpaper(wc);
for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) {
if (!p.isAttached() || !changes.get(p).hasChanged(p)) {
// Again, we're skipping no-ops
@@ -637,7 +835,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (participants.contains(p)) {
topParent = p;
break;
- } else if (reportIfNotTop(p)) {
+ } else if (isWallpaper(p)) {
+ skipIntermediateReports = true;
+ } else if (reportIfNotTop(p) && !skipIntermediateReports) {
tmpList.add(p);
}
}
@@ -707,6 +907,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
/**
+ * A ready group is defined by a root window-container where all transitioning windows under
+ * it are expected to animate together as a group. At the moment, this treats each display as
+ * a ready-group to match the existing legacy transition behavior.
+ */
+ private static boolean isReadyGroup(WindowContainer wc) {
+ return wc instanceof DisplayContent;
+ }
+
+ /**
* Construct a TransitionInfo object from a set of targets and changes. Also populates the
* root surface.
*/
@@ -723,17 +932,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
// Find the top-most shared ancestor of app targets
- WindowContainer ancestor = null;
- for (int i = appTargets.size() - 1; i >= 0; --i) {
- final WindowContainer wc = appTargets.valueAt(i);
- ancestor = wc;
- break;
- }
- if (ancestor == null) {
+ if (appTargets.isEmpty()) {
out.setRootLeash(new SurfaceControl(), 0, 0);
return out;
}
- ancestor = ancestor.getParent();
+ WindowContainer ancestor = appTargets.valueAt(appTargets.size() - 1).getParent();
// Go up ancestor parent chain until all targets are descendants.
ancestorLoop:
@@ -805,6 +1008,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return out;
}
+ boolean getLegacyIsReady() {
+ return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
+ }
+
static Transition fromBinder(IBinder binder) {
return (Transition) binder;
}
@@ -891,6 +1098,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
flags |= FLAG_IS_VOICE_INTERACTION;
}
}
+ final DisplayContent dc = wc.asDisplayContent();
+ if (dc != null) {
+ flags |= FLAG_IS_DISPLAY;
+ }
if (isWallpaper(wc)) {
flags |= FLAG_IS_WALLPAPER;
}
@@ -910,4 +1121,95 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mChildren.addAll(wcs);
}
}
+
+ /**
+ * The transition sync mechanism has 2 parts:
+ * 1. Whether all WM operations for a particular transition are "ready" (eg. did the app
+ * launch or stop or get a new configuration?).
+ * 2. Whether all the windows involved have finished drawing their final-state content.
+ *
+ * A transition animation can play once both parts are complete. This ready-tracker keeps track
+ * of part (1). Currently, WM code assumes that "readiness" (part 1) is grouped. This means that
+ * even if the WM operations in one group are ready, the whole transition itself may not be
+ * ready if there are WM operations still pending in another group. This class helps keep track
+ * of readiness across the multiple groups. Currently, we assume that each display is a group
+ * since that is how it has been until now.
+ */
+ private static class ReadyTracker {
+ private final ArrayMap<WindowContainer, Boolean> mReadyGroups = new ArrayMap<>();
+
+ /**
+ * Ensures that this doesn't report as allReady before it has been used. This is needed
+ * in very niche cases where a transition is a no-op (nothing has been collected) but we
+ * still want to be marked ready (via. setAllReady).
+ */
+ private boolean mUsed = false;
+
+ /**
+ * If true, this overrides all ready groups and reports ready. Used by shell-initiated
+ * transitions via {@link #setAllReady()}.
+ */
+ private boolean mReadyOverride = false;
+
+ /**
+ * Adds a ready-group. Any setReady calls in this subtree will be tracked together. For
+ * now these are only DisplayContents.
+ */
+ void addGroup(WindowContainer wc) {
+ if (mReadyGroups.containsKey(wc)) {
+ Slog.e(TAG, "Trying to add a ready-group twice: " + wc);
+ return;
+ }
+ mReadyGroups.put(wc, false);
+ }
+
+ /**
+ * Sets a group's ready state.
+ * @param wc Any container within a group's subtree. Used to identify the ready-group.
+ */
+ void setReadyFrom(WindowContainer wc, boolean ready) {
+ mUsed = true;
+ WindowContainer current = wc;
+ while (current != null) {
+ if (isReadyGroup(current)) {
+ mReadyGroups.put(current, ready);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Setting Ready-group to"
+ + " %b. group=%s from %s", ready, current, wc);
+ break;
+ }
+ current = current.getParent();
+ }
+ }
+
+ /** Marks this as ready regardless of individual groups. */
+ void setAllReady() {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Setting allReady override");
+ mUsed = true;
+ mReadyOverride = true;
+ }
+
+ /** @return true if all tracked subtrees are ready. */
+ boolean allReady() {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b "
+ + "override=%b states=[%s]", mUsed, mReadyOverride, groupsToString());
+ if (!mUsed) return false;
+ if (mReadyOverride) return true;
+ for (int i = mReadyGroups.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = mReadyGroups.keyAt(i);
+ if (!wc.isAttached() || !wc.isVisibleRequested()) continue;
+ if (!mReadyGroups.valueAt(i)) return false;
+ }
+ return true;
+ }
+
+ private String groupsToString() {
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < mReadyGroups.size(); ++i) {
+ if (i != 0) b.append(',');
+ b.append(mReadyGroups.keyAt(i)).append(':')
+ .append(mReadyGroups.valueAt(i));
+ }
+ return b.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index cc63c4922c32..16d22787500c 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -17,6 +17,13 @@
package com.android.server.wm;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import android.annotation.NonNull;
@@ -24,14 +31,19 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.WindowManager;
import android.window.IRemoteTransition;
import android.window.ITransitionPlayer;
+import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.util.ArrayList;
@@ -41,15 +53,25 @@ import java.util.ArrayList;
class TransitionController {
private static final String TAG = "TransitionController";
+ // State constants to line-up with legacy app-transition proto expectations.
+ private static final int LEGACY_STATE_IDLE = 0;
+ private static final int LEGACY_STATE_READY = 1;
+ private static final int LEGACY_STATE_RUNNING = 2;
+
private ITransitionPlayer mTransitionPlayer;
final ActivityTaskManagerService mAtm;
+ private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
+ new ArrayList<>();
+
/**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
+ final Lock mRunningLock = new Lock();
+
private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
// clean-up/finish any playing transitions.
for (int i = 0; i < mPlayingTransitions.size(); ++i) {
@@ -57,13 +79,18 @@ class TransitionController {
}
mPlayingTransitions.clear();
mTransitionPlayer = null;
+ mRunningLock.doNotifyLocked();
};
/** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
+ // TODO(b/188595497): remove when not needed.
+ final StatusBarManagerInternal mStatusBar;
+
TransitionController(ActivityTaskManagerService atm) {
mAtm = atm;
+ mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
}
/** @see #createTransition(int, int) */
@@ -76,7 +103,7 @@ class TransitionController {
* Creates a transition. It can immediately collect participants.
*/
@NonNull
- Transition createTransition(@WindowManager.TransitionType int type,
+ private Transition createTransition(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags) {
if (mTransitionPlayer == null) {
throw new IllegalStateException("Shell Transitions not enabled");
@@ -87,6 +114,7 @@ class TransitionController {
mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
mCollectingTransition);
+ dispatchLegacyAppTransitionPending();
return mCollectingTransition;
}
@@ -154,13 +182,18 @@ class TransitionController {
return false;
}
+ @WindowManager.TransitionType
+ int getCollectingTransitionType() {
+ return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
+ }
+
/**
* @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition)
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
- @Nullable WindowContainer trigger) {
- return requestTransitionIfNeeded(type, 0 /* flags */, trigger);
+ @NonNull WindowContainer trigger) {
+ return requestTransitionIfNeeded(type, 0 /* flags */, trigger, trigger /* readyGroupRef */);
}
/**
@@ -168,8 +201,10 @@ class TransitionController {
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
- @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) {
- return requestTransitionIfNeeded(type, flags, trigger, null /* remote */);
+ @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
+ @NonNull WindowContainer readyGroupRef) {
+ return requestTransitionIfNeeded(type, flags, trigger, readyGroupRef,
+ null /* remoteTransition */);
}
private static boolean isExistenceType(@WindowManager.TransitionType int type) {
@@ -180,19 +215,20 @@ class TransitionController {
* If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to
* start it. Collection can start immediately.
* @param trigger if non-null, this is the first container that will be collected
+ * @param readyGroupRef Used to identify which ready-group this request is for.
* @return the created transition if created or null otherwise.
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
- @Nullable IRemoteTransition remoteTransition) {
+ @NonNull WindowContainer readyGroupRef, @Nullable IRemoteTransition remoteTransition) {
if (mTransitionPlayer == null) {
return null;
}
Transition newTransition = null;
if (isCollecting()) {
// Make the collecting transition wait until this request is ready.
- mCollectingTransition.setReady(false);
+ mCollectingTransition.setReady(readyGroupRef, false);
} else {
newTransition = requestStartTransition(createTransition(type, flags),
trigger != null ? trigger.asTask() : null, remoteTransition);
@@ -240,15 +276,21 @@ class TransitionController {
mCollectingTransition.collectExistenceChange(wc);
}
+ /** @see Transition#setOverrideAnimation */
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options) {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.setOverrideAnimation(options);
+ }
+
/** @see Transition#setReady */
- void setReady(boolean ready) {
+ void setReady(WindowContainer wc, boolean ready) {
if (mCollectingTransition == null) return;
- mCollectingTransition.setReady(ready);
+ mCollectingTransition.setReady(wc, ready);
}
/** @see Transition#setReady */
- void setReady() {
- setReady(true);
+ void setReady(WindowContainer wc) {
+ setReady(wc, true);
}
/** @see Transition#finishTransition */
@@ -261,6 +303,7 @@ class TransitionController {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record);
mPlayingTransitions.remove(record);
record.finishTransition();
+ mRunningLock.doNotifyLocked();
}
void moveToPlaying(Transition transition) {
@@ -279,4 +322,109 @@ class TransitionController {
mCollectingTransition = null;
}
+ /**
+ * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy
+ * behaviors.
+ * TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ */
+ void setIsLegacyRecents() {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ }
+
+ void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
+ final Transition transition = Transition.fromBinder(token);
+ if (transition == null || !mPlayingTransitions.contains(transition)) {
+ Slog.e(TAG, "Transition isn't playing: " + token);
+ return;
+ }
+ transition.legacyRestoreNavigationBarFromApp();
+ }
+
+ void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) {
+ mLegacyListeners.add(listener);
+ }
+
+ void dispatchLegacyAppTransitionPending() {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionPendingLocked();
+ }
+ }
+
+ void dispatchLegacyAppTransitionStarting(TransitionInfo info) {
+ final boolean keyguardGoingAway = info.getType() == TRANSIT_KEYGUARD_GOING_AWAY
+ || (info.getFlags() & (TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION)) != 0;
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+ 0 /* durationHint */, SystemClock.uptimeMillis(),
+ AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+ }
+ }
+
+ void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+ }
+ }
+
+ void dispatchLegacyAppTransitionCancelled() {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionCancelledLocked(
+ false /* keyguardGoingAway */);
+ }
+ }
+
+ void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ int state = LEGACY_STATE_IDLE;
+ if (!mPlayingTransitions.isEmpty()) {
+ state = LEGACY_STATE_RUNNING;
+ } else if (mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) {
+ state = LEGACY_STATE_READY;
+ }
+ proto.write(AppTransitionProto.APP_TRANSITION_STATE, state);
+ proto.end(token);
+ }
+
+ class Lock {
+ private int mTransitionWaiters = 0;
+ void runWhenIdle(long timeout, Runnable r) {
+ synchronized (mAtm.mGlobalLock) {
+ if (!inTransition()) {
+ r.run();
+ return;
+ }
+ mTransitionWaiters += 1;
+ }
+ final long startTime = SystemClock.uptimeMillis();
+ final long endTime = startTime + timeout;
+ while (true) {
+ synchronized (mAtm.mGlobalLock) {
+ if (!inTransition() || SystemClock.uptimeMillis() > endTime) {
+ mTransitionWaiters -= 1;
+ r.run();
+ return;
+ }
+ }
+ synchronized (this) {
+ try {
+ this.wait(timeout);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+
+ void doNotifyLocked() {
+ synchronized (this) {
+ if (mTransitionWaiters > 0) {
+ this.notifyAll();
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b1c7e196b70c..ffee0b795aec 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -851,7 +851,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
boolean isAttached() {
- return getDisplayArea() != null;
+ WindowContainer parent = getParent();
+ return parent != null && parent.isAttached();
}
void setWaitingForDrawnIfResizingChanged() {
@@ -1671,6 +1672,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return false;
}
+ boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).forAllLeafTaskFragments(callback)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* For all root tasks at or below this container call the callback.
*
@@ -1726,6 +1736,28 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ /**
+ * For all task fragments at or below this container call the callback.
+ *
+ * @param callback Callback to be called for every task.
+ */
+ void forAllTaskFragments(Consumer<TaskFragment> callback) {
+ forAllTaskFragments(callback, true /*traverseTopToBottom*/);
+ }
+
+ void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+
void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
final int count = mChildren.size();
if (traverseTopToBottom) {
@@ -1739,6 +1771,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+
/**
* For all root tasks at or below this container call the callback.
*
@@ -3075,6 +3120,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/** Cheap way of doing cast and instanceof. */
+ TaskFragment asTaskFragment() {
+ return null;
+ }
+
+ /** Cheap way of doing cast and instanceof. */
WindowToken asWindowToken() {
return null;
}
@@ -3282,6 +3332,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * Special helper to check that all windows are synced (vs just top one). This is only
+ * used to differentiate between starting-window vs full-drawn in activity-metrics reporting.
+ */
+ boolean allSyncFinished() {
+ if (!isVisibleRequested()) return true;
+ if (mSyncState != SYNC_STATE_READY) return false;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (!child.allSyncFinished()) return false;
+ }
+ return true;
+ }
+
+ /**
* Called during reparent to handle sync state when the hierarchy changes.
* If this is in a sync group and gets reparented out, it will cancel syncing.
* If this is not in a sync group and gets parented into one, it will prepare itself.
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index ffd6d21c1026..baea85439582 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -89,6 +89,11 @@ public class WindowFrames {
final Rect mCompatFrame = new Rect();
/**
+ * {@code true} if the window frame is a simulated frame and attached to a decor window.
+ */
+ boolean mIsSimulatingDecorWindow = false;
+
+ /**
* Whether the parent frame would have been different if there was no display cutout.
*/
private boolean mParentFrameWasClippedByDisplayCutout;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 47087cfbd147..5bc4d4997f17 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -40,6 +40,7 @@ import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import java.util.List;
+import java.util.Set;
/**
* Window manager local system service interface.
@@ -54,17 +55,18 @@ public abstract class WindowManagerInternal {
*/
public interface AccessibilityControllerInternal {
/**
- * Enable the accessibility trace logging.
+ * Start tracing for the given logging types.
+ * @param loggingTypeFlags flags of the logging types enabled.
*/
- void startTrace();
+ void startTrace(long loggingTypeFlags);
/**
- * Disable the accessibility trace logging.
+ * Disable accessibility tracing for all logging types.
*/
void stopTrace();
/**
- * Is trace enabled or not.
+ * Is tracing enabled for any logging type.
*/
boolean isAccessibilityTracingEnabled();
@@ -73,20 +75,23 @@ public abstract class WindowManagerInternal {
*
* @param where A string to identify this log entry, which can be used to filter/search
* through the tracing file.
+ * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
* @param callingParams The parameters for the method to be logged.
* @param a11yDump The proto byte array for a11y state when the entry is generated.
* @param callingUid The calling uid.
* @param stackTrace The stack trace, null if not needed.
+ * @param ignoreStackEntries The stack entries can be removed
*/
void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace);
+ String where, long loggingTypeFlags, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries);
/**
* Add an accessibility trace entry.
*
* @param where A string to identify this log entry, which can be used to filter/search
* through the tracing file.
+ * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
* @param callingParams The parameters for the method to be logged.
* @param a11yDump The proto byte array for a11y state when the entry is generated.
* @param callingUid The calling uid.
@@ -94,9 +99,11 @@ public abstract class WindowManagerInternal {
* @param timeStamp The time when the method to be logged is called.
* @param processId The calling process Id.
* @param threadId The calling thread Id.
+ * @param ignoreStackEntries The stack entries can be removed
*/
- void logTrace(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callStack, long timeStamp, int processId, long threadId);
+ void logTrace(String where, long loggingTypeFlags, String callingParams,
+ byte[] a11yDump, int callingUid, StackTraceElement[] callStack, long timeStamp,
+ int processId, long threadId, Set<String> ignoreStackEntries);
}
/**
@@ -115,6 +122,16 @@ public abstract class WindowManagerInternal {
*/
void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);
+
+ /**
+ * Called when the display is reparented and becomes an embedded
+ * display. The {@link WindowsForAccessibilityCallback} with the given embedded
+ * display will be replaced by the {@link WindowsForAccessibilityCallback}
+ * associated with its parent display at the same time.
+ *
+ * @param embeddedDisplayId The embedded display Id.
+ */
+ void onDisplayReparented(int embeddedDisplayId);
}
/**
@@ -143,11 +160,11 @@ public abstract class WindowManagerInternal {
void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
/**
- * Notifies that the rotation changed.
+ * Notifies that the display size is changed when rotation or the
+ * logical display is changed.
*
- * @param rotation The current rotation.
*/
- void onRotationChanged(int rotation);
+ void onDisplaySizeChanged();
/**
* Notifies that the context of the user changed. For example, an application
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1ec9187d7a76..16e28d4e48b6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -47,6 +47,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_P
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -1804,7 +1805,8 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
- if (activity != null && activity.isVisible()
+ if (mAtmService.getTransitionController().getTransitionPlayer() == null
+ && activity != null && activity.isVisible()
&& !prepareWindowReplacementTransition(activity)) {
// If not, check if need to set up a dummy transition during display freeze
// so that the unfreeze wait for the apps to draw. This might be needed if
@@ -2478,7 +2480,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}
- if (displayPolicy.areSystemBarsForcedShownLw(win)) {
+ if (displayPolicy.areSystemBarsForcedShownLw()) {
result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
}
if (!win.isGoneForLayout()) {
@@ -2524,7 +2526,8 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (winAnimator.mSurfaceController != null) {
- win.calculateSurfaceBounds(win.getAttrs(), mTmpRect);
+ win.calculateSurfaceBounds(win.getLayoutingAttrs(
+ win.getWindowConfiguration().getRotation()), mTmpRect);
outSurfaceSize.set(mTmpRect.width(), mTmpRect.height());
}
getInsetsSourceControls(win, outActiveControls);
@@ -2717,8 +2720,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
- Bundle options) {
+ public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
+ type, int displayId, Bundle options) {
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2729,15 +2732,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (dc == null) {
ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach"
+ " to a non-existing display:%d", displayId);
- return false;
+ return null;
}
// TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
// the feature b/155340867 is completed.
final DisplayArea da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */);
+ // TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done
+ // in return value of attachWindowContextToDisplayArea.
mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
callingUid, type, options);
- return true;
+ return da.getConfiguration();
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -5390,6 +5395,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
+ if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.setSandboxDisplayApis(sandboxDisplayApis);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/** The global settings only apply to default display. */
private boolean applyForcedPropertiesForDefaultDisplay() {
boolean changed = false;
@@ -7571,6 +7595,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void registerAppTransitionListener(AppTransitionListener listener) {
synchronized (mGlobalLock) {
getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener);
+ mAtmService.getTransitionController().registerLegacyListener(listener);
}
}
@@ -7840,7 +7865,10 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public boolean isTouchOrFaketouchDevice() {
synchronized (mGlobalLock) {
- // All touchable devices are also faketouchable.
+ if (mIsTouchDevice && !mIsFakeTouchDevice) {
+ throw new IllegalStateException(
+ "touchscreen supported device must report faketouch.");
+ }
return mIsFakeTouchDevice;
}
}
@@ -8094,11 +8122,21 @@ public class WindowManagerService extends IWindowManager.Stub
// This could prevent if there is no container animation, we still have to apply the
// pending transaction and exit waiting.
mAnimator.mNotifyWhenNoAnimation = true;
+ boolean animateStarting = false;
while (timeoutRemaining > 0) {
+ // Waiting until all starting windows has finished animating.
+ animateStarting = mRoot.forAllActivities(a -> {
+ return a.hasStartingWindow();
+ });
boolean isAnimating = mAnimator.isAnimationScheduled()
- || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL);
+ || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL)
+ || animateStarting;
if (!isAnimating) {
- break;
+ // isAnimating is a legacy transition query and will be removed, so also add
+ // a check for whether this is in a shell-transition when not using legacy.
+ if (!mAtmService.getTransitionController().inTransition()) {
+ break;
+ }
}
long startTime = System.currentTimeMillis();
try {
@@ -8112,13 +8150,14 @@ public class WindowManagerService extends IWindowManager.Stub
WindowContainer animatingContainer;
animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
ANIMATION_TYPE_ALL);
- if (mAnimator.isAnimationScheduled() || animatingContainer != null) {
+ if (mAnimator.isAnimationScheduled() || animatingContainer != null || animateStarting) {
Slog.w(TAG, "Timed out waiting for animations to complete,"
+ " animatingContainer=" + animatingContainer
+ " animationType=" + SurfaceAnimator.animationTypeToString(
animatingContainer != null
? animatingContainer.mSurfaceAnimator.getAnimationType()
- : SurfaceAnimator.ANIMATION_TYPE_NONE));
+ : SurfaceAnimator.ANIMATION_TYPE_NONE)
+ + " animateStarting=" + animateStarting);
}
}
}
@@ -8160,11 +8199,11 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
true /* includingParents */);
}
- handleTaskFocusChange(touchedWindow.getTask());
+ handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
}
@VisibleForTesting
- void handleTaskFocusChange(Task task) {
+ void handleTaskFocusChange(Task task, ActivityRecord touchedActivity) {
if (task == null) {
return;
}
@@ -8183,7 +8222,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mAtmService.setFocusedTask(task.mTaskId);
+ mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
}
/**
@@ -8556,7 +8595,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (win.mActivityRecord == null || !win.mActivityRecord.isState(
- Task.ActivityState.RESUMED)) {
+ ActivityRecord.State.RESUMED)) {
mDisplayHashController.sendDisplayHashError(callback,
DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
@@ -8611,4 +8650,23 @@ public class WindowManagerService extends IWindowManager.Stub
return snapshot != null && snapshot.hasImeSurface();
}
}
+
+ @Override
+ public int getImeDisplayId() {
+ // TODO(b/189805422): Add a toast to notify users that IMS may get extra
+ // onConfigurationChanged callback when perDisplayFocus is enabled.
+ // Enabling perDisplayFocus means that we track focus on each display, so we don't have
+ // the "top focus" display and getTopFocusedDisplayContent returns the default display
+ // as the fallback. It leads to InputMethodService receives an extra onConfiguration
+ // callback when InputMethodService move from a secondary display to another display
+ // with the same display metrics because InputMethodService will always associate with
+ // the ImeContainer on the default display in onCreate and receive a configuration update
+ // to match default display ImeContainer and then receive another configuration update
+ // from attachToWindowToken.
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getTopFocusedDisplayContent();
+ return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId()
+ : DEFAULT_DISPLAY;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index a94fd074ff2e..d59654949a27 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,6 +19,12 @@ package com.android.server.wm;
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -36,6 +42,7 @@ import com.android.internal.os.ByteTransferPipe;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -58,10 +65,12 @@ public class WindowManagerShellCommand extends ShellCommand {
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
+ private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
+ mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -113,6 +122,14 @@ public class WindowManagerShellCommand extends ShellCommand {
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
+ case "set-letterbox-style":
+ return runSetLetterboxStyle(pw);
+ case "get-letterbox-style":
+ return runGetLetterboxStyle(pw);
+ case "reset-letterbox-style":
+ return runResetLetterboxStyle(pw);
+ case "set-sandbox-display-apis":
+ return runSandboxDisplayApis(pw);
case "set-multi-window-config":
return runSetMultiWindowConfig();
case "get-multi-window-config":
@@ -331,6 +348,37 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ /**
+ * Override display size and metrics to reflect the DisplayArea of the calling activity.
+ */
+ private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean sandboxDisplayApis;
+ switch (arg) {
+ case "true":
+ case "1":
+ sandboxDisplayApis = true;
+ break;
+ case "false":
+ case "0":
+ sandboxDisplayApis = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
+ + "get " + arg);
+ return -1;
+ }
+
+ mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
+ return 0;
+ }
+
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
@@ -548,6 +596,231 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+ final float aspectRatio;
+ try {
+ String arg = getNextArgRequired();
+ aspectRatio = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad aspect ratio format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or aspect ratio should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
+ final int cornersRadius;
+ try {
+ String arg = getNextArgRequired();
+ cornersRadius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad corners radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or corners radius should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+ break;
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+ break;
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+ break;
+ case "wallpaper":
+ backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color;
+ try {
+ String arg = getNextArgRequired();
+ color = Color.valueOf(Color.parseColor(arg));
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or color in #RRGGBB format should be provided as "
+ + "an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundColor(color);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
+ throws RemoteException {
+ final int radius;
+ try {
+ String arg = getNextArgRequired();
+ radius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: blur radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or blur radius should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
+ throws RemoteException {
+ final float alpha;
+ try {
+ String arg = getNextArgRequired();
+ alpha = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad alpha format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or alpha should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
+ }
+ return 0;
+ }
+
+ private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
+ final float multiplier;
+ try {
+ String arg = getNextArgRequired();
+ multiplier = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad multiplier format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or multiplier should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ getErrPrintWriter().println("Error: No arguments provided.");
+ }
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "--aspectRatio":
+ runSetFixedOrientationLetterboxAspectRatio(pw);
+ break;
+ case "--cornerRadius":
+ runSetLetterboxActivityCornersRadius(pw);
+ break;
+ case "--backgroundType":
+ runSetLetterboxBackgroundType(pw);
+ break;
+ case "--backgroundColor":
+ runSetLetterboxBackgroundColor(pw);
+ break;
+ case "--wallpaperBlurRadius":
+ runSetLetterboxBackgroundWallpaperBlurRadius(pw);
+ break;
+ case "--wallpaperDarkScrimAlpha":
+ runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+ break;
+ case "--horizontalPositionMultiplier":
+ runSeLetterboxHorizontalPositionMultiplier(pw);
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ resetLetterboxStyle();
+ }
+ synchronized (mInternal.mGlobalLock) {
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "aspectRatio":
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ break;
+ case "cornerRadius":
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ break;
+ case "backgroundType":
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ break;
+ case "backgroundColor":
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ break;
+ case "wallpaperBlurRadius":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ break;
+ case "wallpaperDarkScrimAlpha":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ break;
+ case "horizontalPositionMultiplier":
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ }
+ return 0;
+ }
+
private int runSetMultiWindowConfig() {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -622,6 +895,40 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private void resetLetterboxStyle() {
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ }
+ }
+
+ private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ synchronized (mInternal.mGlobalLock) {
+ pw.println("Corner radius: "
+ + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
+ pw.println("Horizontal position multiplier: "
+ + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+ pw.println("Aspect ratio: "
+ + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+
+ pw.println("Background type: "
+ + LetterboxConfiguration.letterboxBackgroundTypeToString(
+ mLetterboxConfiguration.getLetterboxBackgroundType()));
+ pw.println(" Background color: " + Integer.toHexString(
+ mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
+ pw.println(" Wallpaper blur radius: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
+ pw.println(" Wallpaper dark scrim alpha: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+ }
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -646,6 +953,12 @@ public class WindowManagerShellCommand extends ShellCommand {
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
+ // set-letterbox-style
+ resetLetterboxStyle();
+
+ // set-sandbox-display-apis
+ mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
+
// set-multi-window-config
runResetMultiWindowConfig();
@@ -680,7 +993,12 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
+ pw.println(" set-sandbox-display-apis [true|1|false|0]");
+ pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
+ pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
+ pw.println(" Size Compat Mode.");
+ printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
@@ -693,6 +1011,49 @@ public class WindowManagerShellCommand extends ShellCommand {
}
}
+ private void printLetterboxHelp(PrintWriter pw) {
+ pw.println(" set-letterbox-style");
+ pw.println(" Sets letterbox style using the following options:");
+ pw.println(" --aspectRatio aspectRatio");
+ pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+ + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+ pw.println(" be ignored and framework implementation will determine aspect ratio.");
+ pw.println(" --cornerRadius radius");
+ pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
+ pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
+ pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" --backgroundType [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating|wallpaper]");
+ pw.println(" Type of background used in the letterbox mode.");
+ pw.println(" --backgroundColor color");
+ pw.println(" Color of letterbox which is be used when letterbox background type");
+ pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
+ pw.println(" letterbox background type. See Color#parseColor for allowed color");
+ pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+ pw.println(" --wallpaperBlurRadius radius");
+ pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
+ pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
+ pw.println(" are ignored and 0 is used.");
+ pw.println(" --wallpaperDarkScrimAlpha alpha");
+ pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
+ pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
+ pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
+ pw.println(" and 0.0 (transparent) is used instead.");
+ pw.println(" --horizontalPositionMultiplier multiplier");
+ pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
+ pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
+ pw.println(" are ignored and central position (0.5) is used.");
+ pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
+ pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
+ pw.println(" |horizontalPositionMultiplier]");
+ pw.println(" Resets overrides to default values for specified properties separated");
+ pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
+ pw.println(" If no arguments provided, all values will be reset.");
+ pw.println(" get-letterbox-style");
+ pw.println(" Prints letterbox style configuration.");
+ }
+
private void printMultiWindowConfigHelp(PrintWriter pw) {
pw.println(" set-multi-window-config");
pw.println(" Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a82a478f2a4f..4fa3aabaabbc 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -16,13 +16,19 @@
package com.android.server.wm;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -34,6 +40,7 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -42,14 +49,17 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizerController;
+import android.window.ITaskFragmentOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
+import android.window.TaskFragmentCreationParams;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,14 +105,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final TaskOrganizerController mTaskOrganizerController;
final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
+ final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
final TransitionController mTransitionController;
+ /**
+ * A Map which manages the relationship between
+ * {@link TaskFragmentCreationParams.mFragmentToken fragmentToken} and {@link TaskFragment}
+ */
+ private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
+ mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
mTransitionController = new TransitionController(atm);
}
@@ -126,10 +143,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
}
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- applyTransaction(t, -1 /*syncId*/, null /*transition*/);
+ applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -143,6 +161,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
}
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -162,7 +181,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (callback != null) {
syncId = startSyncWithOrganizer(callback);
}
- applyTransaction(t, syncId, null /*transition*/);
+ applyTransaction(t, syncId, null /*transition*/, caller);
if (syncId >= 0) {
setSyncReady(syncId);
}
@@ -177,6 +196,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
public IBinder startTransition(int type, @Nullable IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
enforceTaskPermission("startTransition()");
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -196,7 +216,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new IllegalArgumentException("Can't use legacy transitions in"
+ " compatibility mode with no WCT.");
}
- applyTransaction(t, -1 /* syncId */, null);
+ applyTransaction(t, -1 /* syncId */, null, caller);
return null;
}
transition = mTransitionController.createTransition(type);
@@ -205,9 +225,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t == null) {
t = new WindowContainerTransaction();
}
- applyTransaction(t, -1 /*syncId*/, transition);
+ applyTransaction(t, -1 /*syncId*/, transition, caller);
if (needsSetReady) {
- transition.setReady();
+ transition.setAllReady();
}
return transition;
}
@@ -221,6 +241,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
@Nullable WindowContainerTransaction t,
@Nullable IWindowContainerTransactionCallback callback) {
enforceTaskPermission("finishTransition()");
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -231,7 +252,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// apply the incoming transaction before finish in case it alters the visibility
// of the participants.
if (t != null) {
- applyTransaction(t, syncId, null /*transition*/);
+ applyTransaction(t, syncId, null /*transition*/, caller);
}
getTransitionController().finishTransition(transitionToken);
if (syncId >= 0) {
@@ -247,12 +268,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
/**
* @param syncId If non-null, this will be a sync-transaction.
* @param transition A transition to collect changes into.
+ * @param caller Info about the calling process.
*/
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
- @Nullable Transition transition) {
+ @Nullable Transition transition, @Nullable CallerInfo caller) {
int effects = 0;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
if (transition != null) {
// First check if we have a display rotation transition and if so, update it.
@@ -303,7 +326,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final boolean isInLockTaskMode = mService.isInLockTaskMode();
for (int i = 0; i < hopSize; ++i) {
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
- isInLockTaskMode);
+ isInLockTaskMode, caller);
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -341,6 +364,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
task.setMainWindowSizeChangeTransaction(sft);
}
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
// Already calls ensureActivityConfig
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -362,6 +386,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
}
} finally {
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.continueWindowLayout();
}
}
@@ -458,7 +483,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
- int syncId, @Nullable Transition transition, boolean isInLockTaskMode) {
+ int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
+ @Nullable CallerInfo caller) {
final int type = hop.getType();
switch (type) {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
@@ -480,7 +506,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
} else if (!task.mCreatedByOrganizer) {
throw new UnsupportedOperationException(
"Cannot set non-organized task as adjacent flag root: " + wc);
- } else if (task.mAdjacentTask == null) {
+ } else if (task.getAdjacentTaskFragment() == null) {
throw new UnsupportedOperationException(
"Cannot set non-adjacent task as adjacent flag root: " + wc);
}
@@ -501,13 +527,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return effects;
}
+ final WindowContainer wc;
+ final IBinder fragmentToken;
switch (type) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
break;
case HIERARCHY_OP_TYPE_REORDER:
case HIERARCHY_OP_TYPE_REPARENT:
- final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ wc = WindowContainer.fromBinder(hop.getContainer());
if (wc == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
break;
@@ -537,11 +565,68 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
+ mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "launchTask HierarchyOp");
final Bundle launchOpts = hop.getLaunchOptions();
final int taskId = launchOpts.getInt(
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- mService.startActivityFromRecents(taskId, launchOpts);
+ final SafeActivityOptions safeOptions = caller != null
+ ? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid)
+ : SafeActivityOptions.fromBundle(launchOpts);
+ mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
+ taskId, safeOptions);
+ break;
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ final TaskFragmentCreationParams taskFragmentCreationOptions =
+ hop.getTaskFragmentCreationOptions();
+ createTaskFragment(taskFragmentCreationOptions);
+ break;
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+ wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
+ break;
+ }
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment == null || taskFragment.asTask() != null) {
+ throw new IllegalArgumentException(
+ "Can only delete organized TaskFragment, but not Task.");
+ }
+ deleteTaskFragment(taskFragment);
+ break;
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+ fragmentToken = hop.getContainer();
+ if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
+ throw new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token");
+ }
+ final Intent activityIntent = hop.getActivityIntent();
+ final Bundle activityOptions = hop.getLaunchOptions();
+ mService.getActivityStartController()
+ .startActivityInTaskFragment(mLaunchTaskFragments.get(fragmentToken),
+ activityIntent, activityOptions);
+ // TODO(b/189385246) : report the failure back to the organizer if the activity
+ // start failed
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+ fragmentToken = hop.getNewParent();
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
+ if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
+ throw new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token or activity.");
+ }
+ activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
+ final WindowContainer newParent = WindowContainer.fromBinder(hop.getNewParent());
+ if (oldParent == null || !oldParent.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + oldParent);
+ break;
+ }
+ reparentTaskFragment(oldParent, newParent);
break;
}
return effects;
@@ -704,13 +789,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
- final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask();
- final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask();
+ final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
+ final TaskFragment root2 =
+ WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment();
if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
- root1.setAdjacentTask(root2);
+ root1.setAdjacentTaskFragment(root2);
return TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -747,6 +833,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return mDisplayAreaOrganizerController;
}
+ @Override
+ public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() {
+ return mTaskFragmentOrganizerController;
+ }
+
@VisibleForTesting
int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
@@ -795,7 +886,81 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
+ /** Whether the configuration changes are important to report back to an organizer. */
+ static boolean configurationsAreEqualForOrganizer(
+ Configuration newConfig, @Nullable Configuration oldConfig) {
+ if (oldConfig == null) {
+ return false;
+ }
+ int cfgChanges = newConfig.diff(oldConfig);
+ final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
+ ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration,
+ true /* compareUndefined */) : 0;
+ if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) {
+ cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ }
+ return (cfgChanges & CONTROLLABLE_CONFIGS) == 0;
+ }
+
private void enforceTaskPermission(String func) {
mService.enforceTaskPermission(func);
}
+
+ void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams) {
+ final ActivityRecord ownerActivity =
+ ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
+ if (ownerActivity == null || ownerActivity.getTask() == null) {
+ // TODO(b/189385246) : report the failure back to the organizer
+ return;
+ }
+ // The ownerActivity has to belong to the same app as the root Activity of the target Task.
+ final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
+ if (rootActivity.getUid() != ownerActivity.getUid()) {
+ // TODO(b/189385246) : report the failure back to the organizer
+ return;
+ }
+ final TaskFragment taskFragment = new TaskFragment(mService,
+ creationParams.getFragmentToken(), true /* createdByOrganizer */);
+ ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
+ taskFragment.setWindowingMode(creationParams.getWindowingMode());
+ taskFragment.setBounds(creationParams.getInitialBounds());
+ taskFragment.setTaskFragmentOrganizer(
+ creationParams.getOrganizer(), ownerActivity.getPid());
+ mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
+ }
+
+ void reparentTaskFragment(@NonNull WindowContainer oldParent,
+ @Nullable WindowContainer newParent) {
+ WindowContainer parent = newParent;
+ if (parent == null && oldParent.asTaskFragment() != null) {
+ parent = oldParent.asTaskFragment().getTask();
+ }
+ if (parent == null) {
+ // TODO(b/189385246) : report the failure back to the organizer
+ return;
+ }
+ while (oldParent.hasChild()) {
+ oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
+ }
+ }
+
+ void deleteTaskFragment(@NonNull TaskFragment taskFragment) {
+ final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
+ if (index < 0) {
+ throw new IllegalArgumentException(
+ "Not allowed to operate with invalid taskFragment");
+ }
+ mLaunchTaskFragments.removeAt(index);
+ taskFragment.removeImmediately();
+ }
+
+ static class CallerInfo {
+ final int mPid;
+ final int mUid;
+
+ CallerInfo() {
+ mPid = Binder.getCallingPid();
+ mUid = Binder.getCallingUid();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1364c72e6275..65b065ab8eab 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -25,6 +25,13 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -32,13 +39,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import android.Manifest;
import android.annotation.NonNull;
@@ -75,6 +75,7 @@ import com.android.server.wm.ActivityTaskManagerService.HotPath;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -219,6 +220,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
+ /** List of "chained" processes that are running remote animations for this process */
+ private final ArrayList<WeakReference<WindowProcessController>> mRemoteAnimationDelegates =
+ new ArrayList<>();
+
// The bits used for mActivityStateFlags.
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
@@ -735,10 +740,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
- final Task task = mPreQTopResumedActivity.getTask();
- if (task != null) {
- boolean userLeaving = task.shouldBeVisible(null);
- task.startPausingLocked(userLeaving, false /* uiSleeping */,
+ final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
+ if (taskFrag != null) {
+ boolean userLeaving = taskFrag.shouldBeVisible(null);
+ taskFrag.startPausing(userLeaving, false /* uiSleeping */,
activity, "top-resumed-changed");
}
}
@@ -991,7 +996,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
- Task.ActivityState bestInvisibleState = DESTROYED;
+ ActivityRecord.State bestInvisibleState = DESTROYED;
boolean allStoppingFinishing = true;
boolean visible = false;
int minTaskLayer = Integer.MAX_VALUE;
@@ -1215,12 +1220,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
hasVisibleActivities = true;
}
- final Task task = r.getTask();
- if (task != null) {
+ final TaskFragment taskFragment = r.getTaskFragment();
+ if (taskFragment != null) {
// There may be a pausing activity that hasn't shown any window and was requested
// to be hidden. But pausing is also a visible state, it should be regarded as
// visible, so the caller can know the next activity should be resumed.
- hasVisibleActivities |= task.handleAppDied(this);
+ hasVisibleActivities |= taskFragment.handleAppDied(this);
}
r.handleAppDied();
}
@@ -1596,11 +1601,38 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
updateRunningRemoteOrRecentsAnimation();
}
+ /**
+ * Marks another process as a "delegate" animator. This means that process is doing some part
+ * of a remote animation on behalf of this process.
+ */
+ void addRemoteAnimationDelegate(WindowProcessController delegate) {
+ if (!isRunningRemoteTransition()) {
+ throw new IllegalStateException("Can't add a delegate to a process which isn't itself"
+ + " running a remote animation");
+ }
+ mRemoteAnimationDelegates.add(new WeakReference<>(delegate));
+ }
+
void updateRunningRemoteOrRecentsAnimation() {
+ if (!isRunningRemoteTransition()) {
+ // Clean-up any delegates
+ for (int i = 0; i < mRemoteAnimationDelegates.size(); ++i) {
+ final WindowProcessController delegate = mRemoteAnimationDelegates.get(i).get();
+ if (delegate == null) continue;
+ delegate.setRunningRemoteAnimation(false);
+ delegate.setRunningRecentsAnimation(false);
+ }
+ mRemoteAnimationDelegates.clear();
+ }
+
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::setRunningRemoteAnimation, mListener,
- mRunningRecentsAnimation || mRunningRemoteAnimation));
+ isRunningRemoteTransition()));
+ }
+
+ boolean isRunningRemoteTransition() {
+ return mRunningRecentsAnimation || mRunningRemoteAnimation;
}
/** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c3fc99554bcc..3abe45527004 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -34,6 +34,7 @@ import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -202,6 +203,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.gui.TouchOcclusionMode;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -211,7 +213,6 @@ import android.os.PowerManager.WakeReason;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.TouchOcclusionMode;
import android.os.Trace;
import android.os.WorkSource;
import android.provider.Settings;
@@ -874,6 +875,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
/**
+ * Returns all the requested visibilities.
+ *
+ * @return an {@link InsetsState} as the requested visibilities.
+ */
+ InsetsState getRequestedState() {
+ return mRequestedInsetsState;
+ }
+
+ /**
* @see #getRequestedVisibility(int)
*/
void updateRequestedVisibility(InsetsState state) {
@@ -1259,8 +1269,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
frame.inset(left, top, right, bottom);
}
- void computeFrameAndUpdateSourceFrame() {
- computeFrame();
+ void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) {
+ computeFrame(displayFrames);
// Update the source frame to provide insets to other windows during layout. If the
// simulated frames exist, then this is not computing a stable result so just skip.
if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) {
@@ -1271,7 +1281,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/**
* Perform standard frame computation. The result can be obtained with getFrame() if so desired.
*/
- void computeFrame() {
+ void computeFrame(DisplayFrames displayFrames) {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -1384,7 +1394,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int fw = windowFrames.mFrame.width();
final int fh = windowFrames.mFrame.height();
- applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
+ applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame,
+ displayFrames);
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
@@ -1477,6 +1488,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mAttrs;
}
+ WindowManager.LayoutParams getLayoutingAttrs(int rotation) {
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ return mAttrs;
+ }
+ final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation;
+ if (paramsForRotation == null || paramsForRotation.length != 4
+ || paramsForRotation[rotation] == null) {
+ return mAttrs;
+ }
+ return paramsForRotation[rotation];
+ }
+
/** Retrieves the flags used to disable system UI functions. */
int getDisableFlags() {
return mDisableFlags;
@@ -1715,6 +1738,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
+ @Nullable TaskFragment getTaskFragment() {
+ return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null;
+ }
+
@Nullable Task getRootTask() {
final Task task = getTask();
if (task != null) {
@@ -1842,9 +1869,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return super.hasContentToDisplay();
}
- @Override
- boolean isVisible() {
- return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
+ private boolean isVisibleByPolicyOrInsets() {
+ return isVisibleByPolicy()
// If we don't have a provider, this window isn't used as a window generating
// insets, so nobody can hide it over the inset APIs.
&& (mControllableInsetProvider == null
@@ -1852,11 +1878,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
+ boolean isVisible() {
+ return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicyOrInsets();
+ }
+
+ @Override
boolean isVisibleRequested() {
- if (shouldCheckTokenVisibleRequested()) {
- return isVisible() && mToken.isVisibleRequested();
+ final boolean localVisibleRequested =
+ wouldBeVisibleRequestedIfPolicyIgnored() && isVisibleByPolicyOrInsets();
+ if (localVisibleRequested && shouldCheckTokenVisibleRequested()) {
+ return mToken.isVisibleRequested();
}
- return isVisible();
+ return localVisibleRequested;
}
/**
@@ -1903,6 +1936,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return !isWallpaper || mToken.isVisible();
}
+ private boolean wouldBeVisibleRequestedIfPolicyIgnored() {
+ final WindowState parent = getParentWindow();
+ final boolean isParentHiddenRequested = parent != null && !parent.isVisibleRequested();
+ if (isParentHiddenRequested || mAnimatingExit || mDestroying) {
+ return false;
+ }
+ final boolean isWallpaper = mToken.asWallpaperToken() != null;
+ return !isWallpaper || mToken.isVisibleRequested();
+ }
+
/**
* Is this window visible, ignoring its app token? It is not visible if there is no surface,
* or we are in the process of running an exit animation that will remove the surface.
@@ -2360,6 +2403,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void removeImmediately() {
+ if (!mRemoved) {
+ // Destroy surface before super call. The general pattern is that the children need
+ // to be removed before the parent (so that the sync-engine tracking works). Since
+ // WindowStateAnimator is a "virtual" child, we have to do it manually here.
+ mWinAnimator.destroySurfaceLocked(getSyncTransaction());
+ }
super.removeImmediately();
if (mRemoved) {
@@ -2401,8 +2450,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
disposeInputChannel();
- mWinAnimator.destroySurfaceLocked(mTmpTransaction);
- mTmpTransaction.apply();
mSession.windowRemovedLocked();
try {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
@@ -2878,9 +2925,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// means we need to intercept touches outside of that window. The dim layer
// user associated with the window (task or root task) will give us the good
// bounds, as they would be used to display the dim layer.
- final Task task = getTask();
- if (task != null) {
- task.getDimBounds(mTmpRect);
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
+ final Task task = taskFragment.asTask();
+ if (task != null) {
+ task.getDimBounds(mTmpRect);
+ } else {
+ mTmpRect.set(taskFragment.getBounds());
+ }
} else if (getRootTask() != null) {
getRootTask().getDimBounds(mTmpRect);
}
@@ -3863,7 +3915,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean forceRelayout = syncRedraw || reportOrientation || isDragResizeChanged();
final DisplayContent displayContent = getDisplayContent();
final boolean alwaysConsumeSystemBars =
- displayContent.getDisplayPolicy().areSystemBarsForcedShownLw(this);
+ displayContent.getDisplayPolicy().areSystemBarsForcedShownLw();
final int displayId = displayContent.getDisplayId();
markRedrawForSyncReported();
@@ -4397,12 +4449,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
- Rect displayFrame) {
+ Rect displayFrame, DisplayFrames displayFrames) {
final int pw = containingFrame.width();
final int ph = containingFrame.height();
final Task task = getTask();
final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
- final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
+ final WindowManager.LayoutParams attrs = getLayoutingAttrs(displayFrames.mRotation);
+ final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
// We need to fit it to the display if either
// a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
@@ -4412,49 +4465,54 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// screen, but SurfaceViews want to be always at a specific location so we don't fit it to
// the display.
final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
- || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
+ || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
float x, y;
int w,h;
final boolean hasCompatScale = hasCompatScale();
- if ((mAttrs.flags & FLAG_SCALED) != 0) {
- if (mAttrs.width < 0) {
+ if ((attrs.flags & FLAG_SCALED) != 0 || mAttrs != attrs) {
+ // For the window with different layout attrs for different rotations, we need to avoid
+ // using requested size. Otherwise, when finishing a simulated rotation, the information
+ // coming from WindowManagerServices to the ViewRootImpl may not contain the correct
+ // value for the new rotation, and there will be a quick flash of wrong layout when the
+ // simulated activity faded out.
+ if (attrs.width < 0) {
w = pw;
} else if (hasCompatScale) {
- w = (int)(mAttrs.width * mGlobalScale + .5f);
+ w = (int) (attrs.width * mGlobalScale + .5f);
} else {
- w = mAttrs.width;
+ w = attrs.width;
}
- if (mAttrs.height < 0) {
+ if (attrs.height < 0) {
h = ph;
} else if (hasCompatScale) {
- h = (int)(mAttrs.height * mGlobalScale + .5f);
+ h = (int) (attrs.height * mGlobalScale + .5f);
} else {
- h = mAttrs.height;
+ h = attrs.height;
}
} else {
- if (mAttrs.width == MATCH_PARENT) {
+ if (attrs.width == MATCH_PARENT) {
w = pw;
} else if (hasCompatScale) {
- w = (int)(mRequestedWidth * mGlobalScale + .5f);
+ w = (int) (mRequestedWidth * mGlobalScale + .5f);
} else {
w = mRequestedWidth;
}
- if (mAttrs.height == MATCH_PARENT) {
+ if (attrs.height == MATCH_PARENT) {
h = ph;
} else if (hasCompatScale) {
- h = (int)(mRequestedHeight * mGlobalScale + .5f);
+ h = (int) (mRequestedHeight * mGlobalScale + .5f);
} else {
h = mRequestedHeight;
}
}
if (hasCompatScale) {
- x = mAttrs.x * mGlobalScale;
- y = mAttrs.y * mGlobalScale;
+ x = attrs.x * mGlobalScale;
+ y = attrs.y * mGlobalScale;
} else {
- x = mAttrs.x;
- y = mAttrs.y;
+ x = attrs.x;
+ y = attrs.y;
}
if (inNonFullscreenContainer && !layoutInParentFrame()) {
@@ -4481,13 +4539,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
// Set mFrame
- Gravity.apply(mAttrs.gravity, w, h, containingFrame,
- (int) (x + mAttrs.horizontalMargin * pw),
- (int) (y + mAttrs.verticalMargin * ph), windowFrames.mFrame);
-
+ Gravity.apply(attrs.gravity, w, h, containingFrame,
+ (int) (x + attrs.horizontalMargin * pw),
+ (int) (y + attrs.verticalMargin * ph), windowFrames.mFrame);
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
- Gravity.applyDisplay(mAttrs.gravity, displayFrame, windowFrames.mFrame);
+ Gravity.applyDisplay(attrs.gravity, displayFrame, windowFrames.mFrame);
}
// We need to make sure we update the CompatFrame as it is used for
@@ -5949,6 +6006,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
+ if (mActivityRecord != null
+ && mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && mAttrs.type == TYPE_APPLICATION_STARTING) {
+ mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
+ .notifyStartingWindowDrawn(mActivityRecord);
+ }
+
if (postDrawTransaction != null) {
mSyncTransaction.merge(postDrawTransaction);
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fbfa400ba852..fa32be363ffe 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,9 +16,11 @@
package com.android.server.wm;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -229,6 +231,11 @@ class WindowToken extends WindowContainer<WindowState> {
ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT,
"removeAllWindowsIfPossible: removing win=%s", win);
win.removeIfPossible();
+ if (i > mChildren.size()) {
+ // It's possible for removeIfPossible to delete siblings (for example if it is a
+ // starting window, it will perform operations on the ActivityRecord).
+ i = mChildren.size();
+ }
}
}
@@ -451,9 +458,24 @@ class WindowToken extends WindowContainer<WindowState> {
}
Rect getFixedRotationBarContentFrame(int windowType) {
- return isFixedRotationTransforming()
- ? mFixedRotationTransformState.mBarContentFrames.get(windowType)
- : null;
+ if (!isFixedRotationTransforming()) {
+ return null;
+ }
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ return mFixedRotationTransformState.mBarContentFrames.get(windowType);
+ }
+ final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames;
+ final Rect tmpRect = new Rect();
+ if (windowType == TYPE_NAVIGATION_BAR) {
+ tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR)
+ .getFrame());
+ }
+ if (windowType == TYPE_STATUS_BAR) {
+ tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR)
+ .getFrame());
+ }
+ tmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ return tmpRect;
}
InsetsState getFixedRotationTransformInsetsState() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 53401fd47178..ab4f1f6fee2f 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -30,10 +30,6 @@ cc_library_static {
"BroadcastRadio/TunerCallback.cpp",
"BroadcastRadio/convert.cpp",
"BroadcastRadio/regions.cpp",
- "gnss/GnssConfiguration.cpp",
- "gnss/GnssMeasurement.cpp",
- "gnss/GnssMeasurementCallback.cpp",
- "gnss/Utils.cpp",
"stats/SurfaceFlingerPuller.cpp",
"com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
@@ -122,6 +118,7 @@ cc_defaults {
"libinputflinger",
"libinputflinger_base",
"libinputservice",
+ "libservices.core-gnss",
"libstatshidl",
"libstatspull",
"libstatssocket",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a87b513491f6..1639d82f7875 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -40,6 +40,7 @@
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include "gnss/GnssAntennaInfoCallback.h"
#include "gnss/GnssConfiguration.h"
#include "gnss/GnssMeasurement.h"
#include "gnss/Utils.h"
@@ -60,12 +61,7 @@
static jclass class_location;
static jclass class_gnssNavigationMessage;
-static jclass class_gnssAntennaInfoBuilder;
static jclass class_gnssPowerStats;
-static jclass class_phaseCenterOffset;
-static jclass class_sphericalCorrections;
-static jclass class_arrayList;
-static jclass class_doubleArray;
jobject android::mCallbacksObj = nullptr;
@@ -89,7 +85,6 @@ static jmethodID method_reportGeofenceAddStatus;
static jmethodID method_reportGeofenceRemoveStatus;
static jmethodID method_reportGeofencePauseStatus;
static jmethodID method_reportGeofenceResumeStatus;
-static jmethodID method_reportAntennaInfo;
static jmethodID method_reportNavigationMessages;
static jmethodID method_reportLocationBatch;
static jmethodID method_reportGnssServiceDied;
@@ -123,25 +118,9 @@ static jmethodID method_reportNfwNotification;
static jmethodID method_isInEmergencySession;
static jmethodID method_locationCtor;
static jmethodID method_gnssNavigationMessageCtor;
-static jmethodID method_gnssAntennaInfoBuilderCtor;
static jmethodID method_gnssPowerStatsCtor;
-static jmethodID method_phaseCenterOffsetCtor;
-static jmethodID method_sphericalCorrectionsCtor;
-static jmethodID method_arrayListCtor;
-static jmethodID method_arrayListAdd;
-static jmethodID method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz;
-static jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterOffset;
-static jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections;
-static jmethodID method_gnssAntennaInfoBuilderSetSignalGainCorrections;
-static jmethodID method_gnssAntennaInfoBuilderBuild;
static jmethodID method_setSubHalPowerIndicationCapabilities;
-/*
- * Save a pointer to JavaVm to attach/detach threads executing
- * callback methods that need to make JNI calls.
- */
-JavaVM* android::ScopedJniThreadAttach::sJvm;
-
using android::OK;
using android::sp;
using android::status_t;
@@ -193,7 +172,6 @@ using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss;
@@ -954,208 +932,6 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb(
}
/*
- * GnssAntennaInfoCallback implements the callback methods required for the
- * GnssAntennaInfo interface.
- */
-struct GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
- // Methods from V2_1::GnssAntennaInfoCallback follow.
- Return<void> gnssAntennaInfoCb(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
-
-private:
- jobject translateAllGnssAntennaInfos(
- JNIEnv* env,
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
- jobject translateSingleGnssAntennaInfo(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobject translatePhaseCenterOffset(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobject translatePhaseCenterVariationCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobject translateSignalGainCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobjectArray translate2dDoubleArray(JNIEnv* env,
- const hidl_vec<IGnssAntennaInfoCallback::Row>& array);
- void translateAndReportGnssAntennaInfo(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
- void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
-};
-
-Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
- translateAndReportGnssAntennaInfo(gnssAntennaInfos);
- return Void();
-}
-
-jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
- JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
- jsize numRows = array.size();
- if (numRows == 0) {
- // Empty array
- return NULL;
- }
- jsize numCols = array[0].row.size();
- if (numCols <= 1) {
- // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
- return NULL;
- }
-
- // Allocate array of double arrays
- jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
-
- // Create each double array
- for (uint8_t i = 0; i < numRows; i++) {
- jdoubleArray doubleArray = env->NewDoubleArray(numCols);
- env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
- env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
- env->DeleteLocalRef(doubleArray);
- }
- return returnArray;
-}
-
-jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
- JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
- jobject arrayList = env->NewObject(class_arrayList,
- method_arrayListCtor); // Create new ArrayList instance
-
- for (auto gnssAntennaInfo : gnssAntennaInfos) {
- jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
-
- env->CallBooleanMethod(arrayList, method_arrayListAdd,
- gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
-
- // Delete Local Refs
- env->DeleteLocalRef(gnssAntennaInfoObject);
- }
- return arrayList;
-}
-
-jobject GnssAntennaInfoCallback::translatePhaseCenterOffset(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- jobject phaseCenterOffset =
- env->NewObject(class_phaseCenterOffset, method_phaseCenterOffsetCtor,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
-
- return phaseCenterOffset;
-}
-
-jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
- gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
- return NULL;
- }
-
- jobjectArray phaseCenterVariationCorrectionsArray =
- translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
- jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
- translate2dDoubleArray(env,
- gnssAntennaInfo
- .phaseCenterVariationCorrectionUncertaintyMillimeters);
-
- if (phaseCenterVariationCorrectionsArray == NULL ||
- phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
- return NULL;
- }
-
- jobject phaseCenterVariationCorrections =
- env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
- phaseCenterVariationCorrectionsArray,
- phaseCenterVariationCorrectionsUncertaintiesArray);
-
- env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
- env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
-
- return phaseCenterVariationCorrections;
-}
-
-jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
- gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
- return NULL;
- }
- jobjectArray signalGainCorrectionsArray =
- translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
- jobjectArray signalGainCorrectionsUncertaintiesArray =
- translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
-
- if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
- return NULL;
- }
-
- jobject signalGainCorrections =
- env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
- signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
-
- env->DeleteLocalRef(signalGainCorrectionsArray);
- env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
-
- return signalGainCorrections;
-}
-
-jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- jobject phaseCenterOffset = translatePhaseCenterOffset(env, gnssAntennaInfo);
-
- // Nullable
- jobject phaseCenterVariationCorrections =
- translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
-
- // Nullable
- jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
-
- // Get builder
- jobject gnssAntennaInfoBuilderObject =
- env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
-
- // Set fields
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
- gnssAntennaInfo.carrierFrequencyMHz);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
- phaseCenterVariationCorrections);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetSignalGainCorrections,
- signalGainCorrections);
-
- // build
- jobject gnssAntennaInfoObject =
- env->CallObjectMethod(gnssAntennaInfoBuilderObject, method_gnssAntennaInfoBuilderBuild);
-
- // Delete Local Refs
- env->DeleteLocalRef(phaseCenterOffset);
- env->DeleteLocalRef(phaseCenterVariationCorrections);
- env->DeleteLocalRef(signalGainCorrections);
-
- return gnssAntennaInfoObject;
-}
-
-void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
- JNIEnv* env = getJniEnv();
-
- jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
-
- reportAntennaInfo(env, arrayList);
-
- env->DeleteLocalRef(arrayList);
-}
-
-void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
- env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-/*
* MeasurementCorrectionsCallback implements callback methods of interface
* IMeasurementCorrectionsCallback.hal.
*/
@@ -1515,7 +1291,6 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
"(II)V");
method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
"(II)V");
- method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
method_reportNavigationMessages = env->GetMethodID(
clazz,
"reportNavigationMessage",
@@ -1589,39 +1364,6 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D");
method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D");
- jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder");
- class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder);
- method_gnssAntennaInfoBuilderCtor =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "<init>", "()V");
- method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setCarrierFrequencyMHz",
- "(D)Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderSetPhaseCenterOffset =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterOffset",
- "(Landroid/location/GnssAntennaInfo$PhaseCenterOffset;)"
- "Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterVariationCorrections",
- "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
- "Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderSetSignalGainCorrections =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setSignalGainCorrections",
- "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
- "Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderBuild = env->GetMethodID(class_gnssAntennaInfoBuilder, "build",
- "()Landroid/location/GnssAntennaInfo;");
-
- jclass phaseCenterOffsetClass =
- env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffset");
- class_phaseCenterOffset = (jclass)env->NewGlobalRef(phaseCenterOffsetClass);
- method_phaseCenterOffsetCtor = env->GetMethodID(class_phaseCenterOffset, "<init>", "(DDDDDD)V");
-
- jclass sphericalCorrectionsClass =
- env->FindClass("android/location/GnssAntennaInfo$SphericalCorrections");
- class_sphericalCorrections = (jclass)env->NewGlobalRef(sphericalCorrectionsClass);
- method_sphericalCorrectionsCtor =
- env->GetMethodID(class_sphericalCorrections, "<init>", "([[D[[D)V");
-
jclass gnssPowerStatsClass = env->FindClass("com/android/server/location/gnss/GnssPowerStats");
class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
@@ -1634,16 +1376,9 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
- jclass arrayListClass = env->FindClass("java/util/ArrayList");
- class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
- method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
- method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
-
- jclass doubleArrayClass = env->FindClass("[D");
- class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
-
gnss::GnssConfiguration_class_init_once(env);
gnss::GnssMeasurement_class_init_once(env, clazz);
+ gnss::GnssAntennaInfo_class_init_once(env, clazz);
}
/* Initialization needed at system boot and whenever GNSS service dies. */
@@ -2626,7 +2361,7 @@ static jboolean android_location_gnss_hal_GnssNative_start_antenna_info_listenin
return JNI_FALSE;
}
- sp<GnssAntennaInfoCallback> cbIface = new GnssAntennaInfoCallback();
+ sp<gnss::GnssAntennaInfoCallback> cbIface = new gnss::GnssAntennaInfoCallback(mCallbacksObj);
auto result = gnssAntennaInfoIface->setCallback(cbIface);
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
new file mode 100644
index 000000000000..444cb8b7e955
--- /dev/null
+++ b/services/core/jni/gnss/Android.bp
@@ -0,0 +1,51 @@
+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"],
+}
+
+cc_library_shared {
+ name: "libservices.core-gnss",
+ defaults: ["libservices.core-gnss-libs"],
+
+ cpp_std: "c++2a",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+
+ "-DEGL_EGLEXT_PROTOTYPES",
+ "-DGL_GLEXT_PROTOTYPES",
+ ],
+
+ srcs: [
+ "GnssAntennaInfoCallback.cpp",
+ "GnssConfiguration.cpp",
+ "GnssMeasurement.cpp",
+ "GnssMeasurementCallback.cpp",
+ "Utils.cpp",
+ ],
+}
+
+cc_defaults {
+ name: "libservices.core-gnss-libs",
+ shared_libs: [
+ "libandroid_runtime",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libnativehelper",
+ "libutils",
+ "android.hardware.gnss-V1-cpp",
+ "android.hardware.gnss@1.0",
+ "android.hardware.gnss@1.1",
+ "android.hardware.gnss@2.0",
+ "android.hardware.gnss@2.1",
+ "android.hardware.gnss.measurement_corrections@1.0",
+ "android.hardware.gnss.visibility_control@1.0",
+ ],
+}
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
new file mode 100644
index 000000000000..fbc000b25d26
--- /dev/null
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssAntInfoCbJni"
+
+#include "GnssAntennaInfoCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
+
+namespace {
+jclass class_gnssAntennaInfoBuilder;
+jclass class_phaseCenterOffset;
+jclass class_sphericalCorrections;
+jclass class_arrayList;
+jclass class_doubleArray;
+
+jmethodID method_reportAntennaInfo;
+jmethodID method_gnssAntennaInfoBuilderCtor;
+jmethodID method_phaseCenterOffsetCtor;
+jmethodID method_sphericalCorrectionsCtor;
+jmethodID method_arrayListCtor;
+jmethodID method_arrayListAdd;
+jmethodID method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz;
+jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterOffset;
+jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections;
+jmethodID method_gnssAntennaInfoBuilderSetSignalGainCorrections;
+jmethodID method_gnssAntennaInfoBuilderBuild;
+} // anonymous namespace
+
+void GnssAntennaInfo_class_init_once(JNIEnv* env, jclass& clazz) {
+ method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
+ jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder");
+ class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder);
+ method_gnssAntennaInfoBuilderCtor =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "<init>", "()V");
+ method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setCarrierFrequencyMHz",
+ "(D)Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderSetPhaseCenterOffset =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterOffset",
+ "(Landroid/location/GnssAntennaInfo$PhaseCenterOffset;)"
+ "Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterVariationCorrections",
+ "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
+ "Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderSetSignalGainCorrections =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setSignalGainCorrections",
+ "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
+ "Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderBuild = env->GetMethodID(class_gnssAntennaInfoBuilder, "build",
+ "()Landroid/location/GnssAntennaInfo;");
+
+ jclass phaseCenterOffsetClass =
+ env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffset");
+ class_phaseCenterOffset = (jclass)env->NewGlobalRef(phaseCenterOffsetClass);
+ method_phaseCenterOffsetCtor = env->GetMethodID(class_phaseCenterOffset, "<init>", "(DDDDDD)V");
+
+ jclass sphericalCorrectionsClass =
+ env->FindClass("android/location/GnssAntennaInfo$SphericalCorrections");
+ class_sphericalCorrections = (jclass)env->NewGlobalRef(sphericalCorrectionsClass);
+ method_sphericalCorrectionsCtor =
+ env->GetMethodID(class_sphericalCorrections, "<init>", "([[D[[D)V");
+
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+ method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+ method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+ jclass doubleArrayClass = env->FindClass("[D");
+ class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
+}
+
+Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ translateAndReportGnssAntennaInfo(gnssAntennaInfos);
+ return Void();
+}
+
+jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
+ JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
+ jsize numRows = array.size();
+ if (numRows == 0) {
+ // Empty array
+ return NULL;
+ }
+ jsize numCols = array[0].row.size();
+ if (numCols <= 1) {
+ // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
+ return NULL;
+ }
+
+ // Allocate array of double arrays
+ jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
+
+ // Create each double array
+ for (uint8_t i = 0; i < numRows; i++) {
+ jdoubleArray doubleArray = env->NewDoubleArray(numCols);
+ env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
+ env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
+ env->DeleteLocalRef(doubleArray);
+ }
+ return returnArray;
+}
+
+jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
+ JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ jobject arrayList = env->NewObject(class_arrayList,
+ method_arrayListCtor); // Create new ArrayList instance
+
+ for (auto gnssAntennaInfo : gnssAntennaInfos) {
+ jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
+
+ env->CallBooleanMethod(arrayList, method_arrayListAdd,
+ gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
+
+ // Delete Local Refs
+ env->DeleteLocalRef(gnssAntennaInfoObject);
+ }
+ return arrayList;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterOffset(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ jobject phaseCenterOffset =
+ env->NewObject(class_phaseCenterOffset, method_phaseCenterOffsetCtor,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
+
+ return phaseCenterOffset;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
+ gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
+ return NULL;
+ }
+
+ jobjectArray phaseCenterVariationCorrectionsArray =
+ translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
+ jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
+ translate2dDoubleArray(env,
+ gnssAntennaInfo
+ .phaseCenterVariationCorrectionUncertaintyMillimeters);
+
+ if (phaseCenterVariationCorrectionsArray == NULL ||
+ phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
+ return NULL;
+ }
+
+ jobject phaseCenterVariationCorrections =
+ env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
+ phaseCenterVariationCorrectionsArray,
+ phaseCenterVariationCorrectionsUncertaintiesArray);
+
+ env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
+ env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
+
+ return phaseCenterVariationCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
+ gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
+ return NULL;
+ }
+ jobjectArray signalGainCorrectionsArray =
+ translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
+ jobjectArray signalGainCorrectionsUncertaintiesArray =
+ translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
+
+ if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
+ return NULL;
+ }
+
+ jobject signalGainCorrections =
+ env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
+ signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
+
+ env->DeleteLocalRef(signalGainCorrectionsArray);
+ env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
+
+ return signalGainCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ jobject phaseCenterOffset = translatePhaseCenterOffset(env, gnssAntennaInfo);
+
+ // Nullable
+ jobject phaseCenterVariationCorrections =
+ translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
+
+ // Nullable
+ jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
+
+ // Get builder
+ jobject gnssAntennaInfoBuilderObject =
+ env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
+
+ // Set fields
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
+ gnssAntennaInfo.carrierFrequencyMHz);
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
+ phaseCenterVariationCorrections);
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetSignalGainCorrections,
+ signalGainCorrections);
+
+ // build
+ jobject gnssAntennaInfoObject =
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject, method_gnssAntennaInfoBuilderBuild);
+
+ // Delete Local Refs
+ env->DeleteLocalRef(phaseCenterOffset);
+ env->DeleteLocalRef(phaseCenterVariationCorrections);
+ env->DeleteLocalRef(signalGainCorrections);
+
+ return gnssAntennaInfoObject;
+}
+
+void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ JNIEnv* env = getJniEnv();
+
+ jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
+
+ reportAntennaInfo(env, arrayList);
+
+ env->DeleteLocalRef(arrayList);
+}
+
+void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
+ env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.h b/services/core/jni/gnss/GnssAntennaInfoCallback.h
new file mode 100644
index 000000000000..0fc763336077
--- /dev/null
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
+#include <log/log.h>
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAntennaInfo_class_init_once(JNIEnv* env, jclass& clazz);
+
+/*
+ * GnssAntennaInfoCallback implements the callback methods required for the
+ * GnssAntennaInfo interface.
+ */
+struct GnssAntennaInfoCallback : public android::hardware::gnss::V2_1::IGnssAntennaInfoCallback {
+ GnssAntennaInfoCallback(jobject& callbacksObj) : mCallbacksObj(callbacksObj) {}
+ // Methods from V2_1::GnssAntennaInfoCallback follow.
+ hardware::Return<void> gnssAntennaInfoCb(
+ const hardware::hidl_vec<
+ android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+ gnssAntennaInfos);
+
+private:
+ jobject translateAllGnssAntennaInfos(
+ JNIEnv* env,
+ const hardware::hidl_vec<
+ android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+ gnssAntennaInfos);
+ jobject translateSingleGnssAntennaInfo(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobject translatePhaseCenterOffset(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobject translatePhaseCenterVariationCorrections(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobject translateSignalGainCorrections(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobjectArray translate2dDoubleArray(
+ JNIEnv* env,
+ const hardware::hidl_vec<android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Row>&
+ array);
+ void translateAndReportGnssAntennaInfo(
+ const hardware::hidl_vec<
+ android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+ gnssAntennaInfos);
+ void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
+
+ jobject& mCallbacksObj;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 8c6c673fb5b2..34ae4690e0ea 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -28,6 +28,7 @@ using hardware::gnss::GnssData;
using hardware::gnss::GnssMeasurement;
using hardware::gnss::SatellitePvt;
+namespace {
jclass class_arrayList;
jclass class_clockInfo;
jclass class_correlationVectorBuilder;
@@ -63,6 +64,8 @@ jmethodID method_positionEcef;
jmethodID method_velocityEcef;
jmethodID method_clockInfo;
+} // anonymous namespace
+
void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) {
method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData",
"(Landroid/location/GnssMeasurementsEvent;)V");
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 26f12437a217..32200fdf904e 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -34,6 +34,7 @@
namespace android::gnss {
+namespace {
extern jclass class_gnssMeasurementsEvent;
extern jclass class_gnssMeasurement;
extern jclass class_gnssClock;
@@ -42,6 +43,7 @@ extern jmethodID method_gnssMeasurementsEventCtor;
extern jmethodID method_gnssClockCtor;
extern jmethodID method_gnssMeasurementCtor;
extern jmethodID method_reportMeasurementData;
+} // anonymous namespace
void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz);
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 20e14a919fce..8cbdfb8d294e 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -18,6 +18,12 @@
#include "Utils.h"
+/*
+ * Save a pointer to JavaVm to attach/detach threads executing
+ * callback methods that need to make JNI calls.
+ */
+JavaVM* android::ScopedJniThreadAttach::sJvm;
+
namespace android {
namespace {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index c29de905d370..939a3dcfa32d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -389,9 +389,15 @@ class SecurityLogMonitor implements Runnable {
mCriticalLevelLogged = false;
Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
}
- if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
- + " with ids " + mPendingLogs.get(0).getId()
- + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId());
+ if (DEBUG) {
+ if (mPendingLogs.size() > 0) {
+ Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
+ + " with ids " + mPendingLogs.get(0).getId()
+ + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId());
+ } else {
+ Slog.d(TAG, "0 pending events in the buffer after merging");
+ }
+ }
}
@GuardedBy("mLock")
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 91d4f7e2a24d..763b74b53a53 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -130,8 +131,6 @@ import com.android.server.hdmi.HdmiControlService;
import com.android.server.incident.IncidentCompanionService;
import com.android.server.input.InputManagerService;
import com.android.server.inputmethod.InputMethodManagerService;
-import com.android.server.inputmethod.InputMethodSystemProperty;
-import com.android.server.inputmethod.MultiClientInputMethodManagerService;
import com.android.server.integrity.AppIntegrityManagerService;
import com.android.server.lights.LightsService;
import com.android.server.location.LocationManagerService;
@@ -377,6 +376,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.connectivity.IpConnectivityMetrics";
private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
"com.android.server.media.MediaCommunicationService";
+ private static final String APP_COMPAT_OVERRIDES_SERVICE_CLASS =
+ "com.android.server.compat.overrides.AppCompatOverridesService$Lifecycle";
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
private static final String GAME_MANAGER_SERVICE_CLASS =
@@ -1595,12 +1596,7 @@ public final class SystemServer implements Dumpable {
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
t.traceBegin("StartInputMethodManagerLifecycle");
- if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
- mSystemServiceManager.startService(
- MultiClientInputMethodManagerService.Lifecycle.class);
- } else {
- mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
- }
+ mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
t.traceEnd();
t.traceBegin("StartAccessibilityManagerService");
@@ -2632,6 +2628,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("ArtManagerLocal");
+ LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
+ t.traceEnd();
+
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
t.traceBegin("UwbService");
mSystemServiceManager.startService(UWB_SERVICE_CLASS);
@@ -2650,6 +2650,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("AppCompatOverridesService");
+ mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
+ t.traceEnd();
+
ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
START_BLOB_STORE_SERVICE);
diff --git a/services/net/Android.bp b/services/net/Android.bp
index a822257e1a74..21964dd2a068 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -23,7 +23,6 @@ java_library_static {
],
static_libs: [
"netd-client",
- "netlink-client",
"networkstack-client",
"net-utils-services-common",
],
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index aca48b647efa..ee5a534fe005 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -19,8 +19,8 @@ import android.app.backup.FullBackupDataOutput;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.Environment;
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
index 6f168a3888b3..7b821c0606a0 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -32,6 +32,7 @@
<!-- Load additional APKs onto device -->
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
+ <option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
</target_preparer>
<option name="test-tag" value="AppEnumerationInternalTest" />
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
index 933784560410..63aaf2422116 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
@@ -43,8 +43,16 @@ public class AppEnumerationInternalTests {
private static final String TEST_DATA_PATH = "/data/local/tmp/appenumerationtests/";
private static final String SYNC_PROVIDER_APK_PATH =
TEST_DATA_PATH + "AppEnumerationSyncProviderTestApp.apk";
- private static final String SYNC_PROVIDER_PKG_NAME = "com.android.appenumeration.syncprovider";
- private static final String SYNC_PROVIDER_AUTHORITY = SYNC_PROVIDER_PKG_NAME;
+ private static final String HAS_APPOP_PERMISSION_APK_PATH =
+ TEST_DATA_PATH + "AppEnumerationHasAppOpPermissionTestApp.apk";
+
+ private static final String TARGET_SYNC_PROVIDER = "com.android.appenumeration.syncprovider";
+ private static final String TARGET_HAS_APPOP_PERMISSION =
+ "com.android.appenumeration.hasappoppermission";
+
+ private static final String SYNC_PROVIDER_AUTHORITY = TARGET_SYNC_PROVIDER;
+ private static final String PERMISSION_REQUEST_INSTALL_PACKAGES =
+ "android.permission.REQUEST_INSTALL_PACKAGES";
private IPackageManager mIPackageManager;
@@ -55,7 +63,8 @@ public class AppEnumerationInternalTests {
@After
public void tearDown() throws Exception {
- uninstallPackage(SYNC_PROVIDER_PKG_NAME);
+ uninstallPackage(TARGET_SYNC_PROVIDER);
+ uninstallPackage(TARGET_HAS_APPOP_PERMISSION);
}
@Test
@@ -67,7 +76,7 @@ public class AppEnumerationInternalTests {
assertThat(names).contains(SYNC_PROVIDER_AUTHORITY);
assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
- .contains(SYNC_PROVIDER_PKG_NAME);
+ .contains(TARGET_SYNC_PROVIDER);
}
@Test
@@ -79,7 +88,27 @@ public class AppEnumerationInternalTests {
assertThat(names).doesNotContain(SYNC_PROVIDER_AUTHORITY);
assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
- .doesNotContain(SYNC_PROVIDER_PKG_NAME);
+ .doesNotContain(TARGET_SYNC_PROVIDER);
+ }
+
+ @Test
+ public void getAppOpPermissionPackages_canSeeForceQueryable() throws Exception {
+ installPackage(HAS_APPOP_PERMISSION_APK_PATH, true /* forceQueryable */);
+
+ final String[] packageNames = mIPackageManager.getAppOpPermissionPackages(
+ PERMISSION_REQUEST_INSTALL_PACKAGES);
+
+ assertThat(packageNames).asList().contains(TARGET_HAS_APPOP_PERMISSION);
+ }
+
+ @Test
+ public void getAppOpPermissionPackages_cannotSeeHasAppOpPermission() throws Exception {
+ installPackage(HAS_APPOP_PERMISSION_APK_PATH, false /* forceQueryable */);
+
+ final String[] packageNames = mIPackageManager.getAppOpPermissionPackages(
+ PERMISSION_REQUEST_INSTALL_PACKAGES);
+
+ assertThat(packageNames).asList().doesNotContain(TARGET_HAS_APPOP_PERMISSION);
}
private static void installPackage(String apkPath, boolean forceQueryable) {
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
index 64239b4c6b2d..7aa300b177ee 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
@@ -34,3 +34,17 @@ android_test_helper_app {
test_suites: ["device-tests"],
platform_apis: true,
}
+
+android_test_helper_app {
+ name: "AppEnumerationHasAppOpPermissionTestApp",
+ srcs: ["src/**/*.java"],
+ manifest: "AndroidManifest-hasAppOpPermission.xml",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml
new file mode 100644
index 000000000000..be8a6dc8709c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.appenumeration.hasappoppermission">
+
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 022fadcc6dd0..15dfd266853e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -21,12 +21,15 @@ import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -48,7 +51,10 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
-import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -62,6 +68,8 @@ import java.util.concurrent.TimeUnit;
*/
@RunWith(MockitoJUnitRunner.class)
public class CacheOomRankerTest {
+ private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
+ ZoneOffset.UTC).toInstant();
@Mock
private AppOpsService mAppOpsService;
@@ -136,6 +144,15 @@ public class CacheOomRankerTest {
mExecutor.init();
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+ Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1),
+ false);
+ mExecutor.waitForLatch();
+ assertThat(mCacheOomRanker.mPreserveTopNApps)
+ .isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1);
+
+ mExecutor.init();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f),
false);
@@ -165,6 +182,7 @@ public class CacheOomRankerTest {
@Test
public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 5,
+ /* preserveTopNApps= */ 0,
/* usesWeight= */ 0.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */1.0f);
@@ -172,36 +190,38 @@ public class CacheOomRankerTest {
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(lastUsed40MinutesAgo);
ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(lastUsed42MinutesAgo);
ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
processList.add(lastUsed60MinutesAgo);
ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(lastUsed15MinutesAgo);
ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
processList.add(lastUsed17MinutesAgo);
// Only re-ranking 5 entries so this should stay in most recent position.
ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
processList.add(lastUsed30MinutesAgo);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 5 ordered by least recently used first, then last processes position unchanged.
assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
- lastUsed30MinutesAgo);
+ lastUsed30MinutesAgo).inOrder();
}
@Test
public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
/* usesWeight= */ 0.0f,
/* pssWeight= */ 1.0f,
/* lruWeight= */ 0.0f);
@@ -209,145 +229,328 @@ public class CacheOomRankerTest {
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(rss10k);
ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(rss20k);
ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
processList.add(rss1k);
ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(rss100k);
ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(rss2k);
ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
processList.add(rss15k);
// Only re-ranking 6 entries so this should stay in most recent position.
ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
processList.add(rss16k);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 6 ordered by largest pss, then last processes position unchanged.
assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
- rss16k);
+ rss16k).inOrder();
}
@Test
public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
/* usesWeight= */ 1.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.setLruProcessServiceStartLSP(1);
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(used1000);
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(used2000);
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(used10);
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(used20);
// Only re-ranking 6 entries so last two should stay in most recent position.
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(used500);
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 4 ordered by uses, then last processes position unchanged.
assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
- used200);
+ used200).inOrder();
}
@Test
- public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException {
+ public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
- /* usesWeight= */ 0.5f,
- /* pssWeight= */ 0.2f,
- /* lruWeight= */ 0.3f);
+ /* preserveTopNApps= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
- ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
- processList.add(unknownAdj1);
- ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
- processList.add(unknownAdj2);
- ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
- processList.add(unknownAdj3);
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(foregroundAdj);
ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(serviceAdj);
ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(systemAdj);
+ list.setLruProcessServiceStartLSP(processList.size());
- // 6 Processes but only 3 in eligible for cache so no re-ranking.
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
- // All positions unchanged.
- assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
- foregroundAdj, serviceAdj, systemAdj);
+ // 6 processes, only 3 in eligible for cache, so only those are re-ranked.
+ assertThat(processList).containsExactly(used10, used1000, used2000,
+ foregroundAdj, serviceAdj, systemAdj).inOrder();
}
@Test
- public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException {
+ public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(service1);
+ ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(service2);
+ ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(service3);
+ list.setLruProcessServiceStartLSP(3);
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // Services unchanged, rest re-ranked.
+ assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2,
+ service3).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList set1List = new ProcessList();
+ ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP();
+ ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ set1ProcessList.add(set1Used1000);
+ ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ set1ProcessList.add(set1Used2000);
+ ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ set1ProcessList.add(set1Used10);
+ ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ set1ProcessList.add(set1Uses20);
+ ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ set1ProcessList.add(set1Uses500);
+ ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ set1ProcessList.add(set1Uses200);
+ set1List.setLruProcessServiceStartLSP(set1ProcessList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList,
+ set1List.getLruProcessServiceStartLOSP());
+ assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200,
+ set1Uses500, set1Used1000, set1Used2000).inOrder();
+
+ ProcessList set2List = new ProcessList();
+ ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP();
+ ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ set2ProcessList.add(set2Used1000);
+ ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ set2ProcessList.add(set2Used2000);
+ ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ set2ProcessList.add(set2Used10);
+ ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ set2ProcessList.add(set2ForegroundAdj);
+ ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ set2ProcessList.add(set2ServiceAdj);
+ ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ set2ProcessList.add(set2SystemAdj);
+ set2List.setLruProcessServiceStartLSP(set2ProcessList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList,
+ set2List.getLruProcessServiceStartLOSP());
+ assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000,
+ set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 3,
/* usesWeight= */ 1.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.setLruProcessServiceStartLSP(4);
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(used1000);
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(used2000);
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(used10);
+ // Preserving the top 3 processes, so these should not be re-ranked.
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(used20);
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(used500);
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
- // All positions unchanged.
+ // First 3 ordered by uses, then last processes position unchanged.
+ assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+ used200).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 100,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // Nothing reordered, as we preserve the top 100 apps.
assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
- used200);
+ used200).inOrder();
}
- private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight)
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ -100,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ // Negative preserveTopNApps interpreted as the default (3), so the last three are unranked.
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // First 3 apps re-ranked, as preserveTopNApps is interpreted as 3.
+ assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+ used200).inOrder();
+ }
+
+ private void setConfig(int numberToReRank, int preserveTopNApps, float usesWeight,
+ float pssWeight, float lruWeight)
throws InterruptedException {
mExecutor.init(4);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -355,6 +558,10 @@ public class CacheOomRankerTest {
Integer.toString(numberToReRank),
false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+ Integer.toString(preserveTopNApps),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
Float.toString(lruWeight),
false);
@@ -364,12 +571,12 @@ public class CacheOomRankerTest {
false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
- Float.toString(useWeight),
+ Float.toString(usesWeight),
false);
mExecutor.waitForLatch();
assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
- assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight);
+ assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
}
@@ -382,7 +589,7 @@ public class CacheOomRankerTest {
app.info.uid = mNextPackageUid++;
// Exact value does not mater, it can be any state for which compaction is allowed.
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.mState.setSetAdj(setAdj);
+ app.mState.setCurAdj(setAdj);
app.setLastActivityTime(lastActivityTime);
app.mProfile.setLastRss(lastRss);
app.mState.setCached(false);
@@ -390,6 +597,12 @@ public class CacheOomRankerTest {
app.mState.setCached(false);
app.mState.setCached(true);
}
+ // Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
+ // app is currently launching.
+ ProcessStatsService processStatsService = new ProcessStatsService(
+ mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(),
+ "procstats"));
+ app.makeActive(mock(IApplicationThread.class), processStatsService);
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 4d86c8716bf1..8840057cc552 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -182,7 +182,14 @@ public class GameManagerServiceTests {
}
private void mockDeviceConfigPerformance() {
- String configString = "mode=2,downscaleFactor=0.5";
+ String configString = "mode=2,downscaleFactor=0.5,useAngle=false";
+ when(DeviceConfig.getProperty(anyString(), anyString()))
+ .thenReturn(configString);
+ }
+
+ // ANGLE will be disabled for most apps, so treat enabling ANGLE as a special case.
+ private void mockDeviceConfigPerformanceEnableAngle() {
+ String configString = "mode=2,downscaleFactor=0.5,useAngle=true";
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
}
@@ -212,7 +219,8 @@ public class GameManagerServiceTests {
}
private void mockGameModeOptInAll() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
@@ -224,7 +232,8 @@ public class GameManagerServiceTests {
}
private void mockGameModeOptInPerformance() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
@@ -234,7 +243,8 @@ public class GameManagerServiceTests {
}
private void mockGameModeOptInBattery() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true);
@@ -244,7 +254,8 @@ public class GameManagerServiceTests {
}
private void mockInterventionAllowDownscaleTrue() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, true);
@@ -254,7 +265,8 @@ public class GameManagerServiceTests {
}
private void mockInterventionAllowDownscaleFalse() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, false);
@@ -263,6 +275,27 @@ public class GameManagerServiceTests {
.thenReturn(applicationInfo);
}
+ private void mockInterventionAllowAngleTrue() throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ metaDataBundle.putBoolean(
+ GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, true);
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ }
+
+ private void mockInterventionAllowAngleFalse() throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ metaDataBundle.putBoolean(
+ GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, false);
+ applicationInfo.metaData = metaDataBundle;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ }
+
/**
* By default game mode is not supported.
*/
@@ -427,6 +460,19 @@ public class GameManagerServiceTests {
assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
}
+ private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
+ boolean angleEnabled) {
+ gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+
+ // Validate GamePackageConfiguration returns the correct value.
+ GameManagerService.GamePackageConfiguration config =
+ gameManagerService.getConfig(mPackageName);
+ assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
+
+ // Validate GameManagerService.getAngleEnabled() returns the correct value.
+ assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
+ }
+
/**
* Phenotype device config exists, but is only propagating the default value.
*/
@@ -592,6 +638,50 @@ public class GameManagerServiceTests {
}
/**
+ * PERFORMANCE game mode is configured through Phenotype. The app hasn't specified any metadata.
+ */
+ @Test
+ public void testInterventionAllowAngleDefault() throws Exception {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+ checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false);
+ }
+
+ /**
+ * PERFORMANCE game mode is configured through Phenotype. The app has opted-out of ANGLE.
+ */
+ @Test
+ public void testInterventionAllowAngleFalse() throws Exception {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ mockDeviceConfigPerformanceEnableAngle();
+ mockInterventionAllowAngleFalse();
+ mockModifyGameModeGranted();
+ checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false);
+ }
+
+ /**
+ * PERFORMANCE game mode is configured through Phenotype. The app has redundantly specified
+ * the ANGLE metadata default value of "true".
+ */
+ @Test
+ public void testInterventionAllowAngleTrue() throws Exception {
+ mockDeviceConfigPerformanceEnableAngle();
+ mockInterventionAllowAngleTrue();
+
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true);
+ }
+
+ /**
* PERFORMANCE game mode is configured through Phenotype, but the app has also opted into the
* same mode. No interventions for this game mode should be available in this case.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
new file mode 100644
index 000000000000..24f6b0b6b2b6
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
@@ -0,0 +1 @@
+include /apex/appsearch/OWNERS \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS
new file mode 100644
index 000000000000..f8c3520e9fa8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
new file mode 100644
index 000000000000..bf97042d7b88
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
@@ -0,0 +1,302 @@
+/*
+ * 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.compat.overrides;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test class for {@link AppCompatOverridesParser}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesParserTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesParserTest {
+ private static final String PACKAGE_1 = "com.android.test1";
+ private static final String PACKAGE_2 = "com.android.test2";
+ private static final String PACKAGE_3 = "com.android.test3";
+ private static final String PACKAGE_4 = "com.android.test4";
+
+ private AppCompatOverridesParser mParser;
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mParser = new AppCompatOverridesParser(mPackageManager);
+ }
+
+ @Test
+ public void parseRemoveOverrides_emptyConfig_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+
+ assertThat(mParser.parseRemoveOverrides("", ownedChangeIds)).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasWildcardNoOwnedChangeIds_returnsEmpty() {
+ when(mPackageManager.getInstalledApplications(anyInt()))
+ .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+ assertThat(mParser.parseRemoveOverrides("*", /* ownedChangeIds= */ emptySet())).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasWildcard_returnsAllInstalledPackagesToAllOwnedIds() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+ when(mPackageManager.getInstalledApplications(anyInt()))
+ .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2),
+ createAppInfo(PACKAGE_3)));
+
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides("*", ownedChangeIds);
+
+ assertThat(result).hasSize(3);
+ assertThat(result.get(PACKAGE_1)).containsExactly(123L, 456L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(123L, 456L);
+ assertThat(result.get(PACKAGE_3)).containsExactly(123L, 456L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidWildcardSymbol_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+ when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
+ Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+ assertThat(mParser.parseRemoveOverrides("**", ownedChangeIds)).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasSingleEntry_returnsPackageToChangeIds() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12:34", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L, 34L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasMultipleEntries_returnsPackagesToChangeIds() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L, 56L, 78L));
+
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + "=*," + PACKAGE_3 + "=12:56:78," + PACKAGE_4
+ + "=", ownedChangeIds);
+
+ assertThat(result).hasSize(3);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L, 34L, 56L, 78L);
+ assertThat(result.get(PACKAGE_3)).containsExactly(12L, 56L, 78L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasPackageWithWildcardNoOwnedId_returnsWithoutPackage() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=*," + PACKAGE_2 + "=12", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidKeyValueListFormat_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L));
+
+ assertThat(mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + ">34", ownedChangeIds)).isEmpty();
+ }
+
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + "=12:56L:78," + PACKAGE_3
+ + "=34L", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(2);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L, 78L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_emptyConfig_returnsEmpty() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("")).isEmpty();
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasSingleChangeId_returnsChangeId() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("123")).containsExactly(123L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasMultipleChangeIds_returnsChangeIds() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,34,56")).containsExactly(12L,
+ 34L, 56L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+ // We add a valid entry before and after the invalid ones to make sure they are applied.
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,C34,56")).containsExactly(12L,
+ 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_emptyConfig_returnsEmpty() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).isEmpty();
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithSingleOverride_returnsOverride() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverridesToAdd_returnsOverrides() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,56::2:true,"
+ + "56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,1112::5:true,"
+ + "56:6::true,1112:6:7:false", /* versionCode= */
+ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(6);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(false).build());
+ assertThat(result.overridesToAdd.get(34L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled(
+ true).build());
+ assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled(
+ false).build());
+ assertThat(result.overridesToAdd.get(78L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled(
+ true).build());
+ assertThat(result.overridesToAdd.get(910L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build());
+ assertThat(result.overridesToAdd.get(1112L)).isEqualTo(
+ new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build());
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverridesToRemove_returnsOverrides() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::,34:1:2:", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToRemove).containsExactly(12L, 34L);
+ assertThat(result.overridesToAdd).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithBothOverridesToAddAndRemove_returnsOverrides() {
+ // Note that change 56 is both added and removed, therefore it will only be removed.
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "56:::,12:::true,34:::,56:3:7:true", /* versionCode= */ 5, /* changeIdsToSkip= */
+ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToRemove).containsExactly(34L, 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() {
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+ changeIdsToSkip.add(34L);
+ changeIdsToSkip.add(56L);
+ changeIdsToSkip.add(910L);
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::true,34:::,56:3:7:true,78:::", /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToRemove).containsExactly(78L);
+ }
+
+ @Test
+ public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() {
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+ changeIdsToSkip.add(12L);
+ changeIdsToSkip.add(34L);
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::true,34:::", /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result.overridesToAdd).isEmpty();
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() {
+ // We add a valid entry before and after the invalid ones to make sure they are applied.
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
+ + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:one:ten:",
+ /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(2);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(1).setMaxVersionCode(2).setEnabled(
+ false).build());
+ assertThat(result.overridesToRemove).containsExactly(34L);
+ }
+
+ private static ApplicationInfo createAppInfo(String packageName) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ return appInfo;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
new file mode 100644
index 000000000000..312927206a80
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -0,0 +1,688 @@
+/*
+ * 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.compat.overrides;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Test class for {@link AppCompatOverridesService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesServiceTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesServiceTest {
+ private static final String NAMESPACE_1 = "namespace_1";
+ private static final String NAMESPACE_2 = "namespace_2";
+ private static final String NAMESPACE_3 = "namespace_3";
+ private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(NAMESPACE_1,
+ NAMESPACE_2, NAMESPACE_3);
+
+ private static final String PACKAGE_1 = "com.android.test1";
+ private static final String PACKAGE_2 = "com.android.test2";
+ private static final String PACKAGE_3 = "com.android.test3";
+ private static final String PACKAGE_4 = "com.android.test4";
+ private static final String PACKAGE_5 = "com.android.test5";
+
+ private MockContext mMockContext;
+ private BroadcastReceiver mPackageReceiver;
+ private AppCompatOverridesService mService;
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private IPlatformCompat mPlatformCompat;
+
+ @Captor
+ private ArgumentCaptor<CompatibilityOverrideConfig> mOverridesToAddConfigCaptor;
+ @Captor
+ private ArgumentCaptor<CompatibilityOverridesToRemoveConfig> mOverridesToRemoveConfigCaptor;
+
+ @Rule
+ public TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfigRule();
+
+ class MockContext extends ContextWrapper {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public Executor getMainExecutor() {
+ // Run on current thread
+ return Runnable::run;
+ }
+
+ @Override
+ @Nullable
+ public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
+ @NonNull IntentFilter filter, @Nullable String broadcastPermission,
+ @Nullable Handler scheduler) {
+ mPackageReceiver = receiver;
+ return null;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = new MockContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ mService = new AppCompatOverridesService(mMockContext, mPlatformCompat,
+ SUPPORTED_NAMESPACES);
+ mService.registerPackageReceiver();
+ assertThat(mPackageReceiver).isNotNull();
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagNotSet_appliesPackageOverrides()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 3);
+ mockGetApplicationInfoNotInstalled(PACKAGE_2);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 10);
+ mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 1);
+ mockGetApplicationInfo(PACKAGE_5, /* versionCode= */ 1);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true,456:::")
+ .setString(PACKAGE_4, "")
+ .setString(PACKAGE_5, "123:::,789:::")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789").build());
+
+ Map<Long, PackageOverride> addedOverrides;
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+ assertThat(addedOverrides).hasSize(2);
+ assertThat(addedOverrides.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(addedOverrides.get(456L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+ addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+ assertThat(addedOverrides).hasSize(1);
+ assertThat(addedOverrides.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(10).setMaxVersionCode(
+ 11).setEnabled(false).build());
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ // Package 4
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ // Package 5
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_5));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_5));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagSetBefore_skipsOverridesToRemove()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456," + PACKAGE_2 + "=123")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_4, "123:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "456:::true").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(789L);
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ // Package 4 (not applied because it hasn't changed after the listener was added)
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagChangedNoPackageOverridesFlags_removesOnly()
+ throws Exception {
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES,
+ PACKAGE_1 + "=123:456," + PACKAGE_2 + "=789").build());
+
+ // Package 1
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L);
+ // Package 2
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagAndSomePackageOverrideFlagsChanged_ok()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456")
+ .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_3, "456:::false,789:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_2 + "=123," + PACKAGE_3 + "=789")
+ .setString(PACKAGE_2, "123:::true,456:::").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L,
+ 789L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.size()).isAtLeast(2);
+ assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L);
+ assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L);
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_ownedChangeIdsFlagAndSomePackageOverrideFlagsChanged_ok()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=*")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_3, "456:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
+ .setString(PACKAGE_2, "123:::true").build());
+
+ // Package 1
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L,
+ 789L);
+ // Package 2
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L);
+ // Package 3
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_3));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ }
+
+ @Test
+ public void onPropertiesChanged_platformCompatThrowsExceptionForSomeCalls_skipsFailedCalls()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 0);
+ doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ doThrow(new RemoteException()).when(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456:::")
+ .setString(PACKAGE_2, "123:::true,456:::")
+ .setString(PACKAGE_3, "123:::true,456:::")
+ .setString(PACKAGE_4, "123:::true,456:::").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ // Package 2
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_2));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ // Package 4
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentDataIsNull_doesNothing() throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext, new Intent(ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_actionIsNull_doesNothing() throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, /* action= */ null));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_unsupportedAction_doesNothing() throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_USER_SWITCHED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentPackageNotInstalled_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentNoOverridesForPackage_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_2, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_3, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntent_appliesOverridesFromAllNamespaces()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_2, "102:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_3, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(PACKAGE_1, "301:::true,302:::false")
+ .setString(PACKAGE_2, "302:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, times(2)).putOverridesOnReleaseBuilds(
+ mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
+ assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
+ assertThat(configs.get(1).overrides.keySet()).containsExactly(301L, 302L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+ }
+
+ @Test
+ public void packageReceiver_packageChangedIntent_appliesOverrides()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true,103:::").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_CHANGED));
+
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(
+ mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentRemoveOverridesSetForSomeNamespaces_skipsIds()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=103," + PACKAGE_2 + "=101")
+ .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_2, "102:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=301," + PACKAGE_3 + "=302")
+ .setString(PACKAGE_1, "301:::true,302:::false,303:::")
+ .setString(PACKAGE_3, "302:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
+ mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
+ assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
+ assertThat(configs.get(1).overrides.keySet()).containsExactly(201L);
+ assertThat(configs.get(2).overrides.keySet()).containsExactly(302L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(303L);
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntentNoOverridesForPackage_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102")
+ .setString(PACKAGE_2, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201,202")
+ .setString(PACKAGE_3, "201:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntentPackageInstalledForAnotherUser_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+ .setString(PACKAGE_1, "101:::true,103:::").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201,202")
+ .setString(PACKAGE_1, "202:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntent_removesOwnedOverridesForNamespacesWithPackage()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+ .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_2, "102:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201")
+ .setString(PACKAGE_3, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_OWNED_CHANGE_IDS, "301,302")
+ .setString(PACKAGE_1, "302:::")
+ .setString(PACKAGE_2, "301:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.get(0).changeIds).containsExactly(101L, 102L, 103L);
+ assertThat(configs.get(1).changeIds).containsExactly(301L, 302L);
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntentNoOwnedIdsForSomeNamespace_skipsNamespace()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102")
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_OWNED_CHANGE_IDS, "301")
+ .setString(PACKAGE_1, "301:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.get(0).changeIds).containsExactly(101L, 102L);
+ assertThat(configs.get(1).changeIds).containsExactly(301L);
+ }
+
+ @Test
+ public void packageReceiver_platformCompatThrowsExceptionForSomeNamespace_skipsFailedCall()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(PACKAGE_1, "301:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+ argThat(config -> config.overrides.containsKey(201L)), eq(PACKAGE_1));
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ private void mockGetApplicationInfo(String packageName, long versionCode)
+ throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())).thenReturn(
+ createAppInfo(versionCode));
+ }
+
+ private void mockGetApplicationInfoNotInstalled(String packageName) throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+ .thenThrow(new PackageManager.NameNotFoundException());
+ }
+
+ private static ApplicationInfo createAppInfo(long versionCode) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.longVersionCode = versionCode;
+ return appInfo;
+ }
+
+ private Intent createPackageIntent(String packageName, @Nullable String action) {
+ return new Intent(action, Uri.parse("package:" + packageName));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS
new file mode 100644
index 000000000000..6e8aefc4bc01
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/overrides/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index b2471fac8b45..e0cee4b4bf42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -97,11 +97,6 @@ public class JobSchedulerServiceTest {
super(context);
mAppStateTracker = mock(AppStateTrackerImpl.class);
}
-
- @Override
- public boolean isChainedAttributionEnabled() {
- return false;
- }
}
@Before
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 3dc7bc1154ea..a9853bf94eeb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -152,6 +152,11 @@ public class ConnectivityControllerTest {
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
DataUnit.MEBIBYTES.toBytes(1))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ final JobInfo.Builder jobWithMinChunk = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
+ DataUnit.MEBIBYTES.toBytes(1))
+ .setMinimumNetworkChunkBytes(DataUnit.KIBIBYTES.toBytes(100))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -170,18 +175,51 @@ public class ConnectivityControllerTest {
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
.setLinkDownstreamBandwidthKbps(1).build(), mConstants));
+ assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow downstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
.setLinkDownstreamBandwidthKbps(1).build(), mConstants));
+ assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow upstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
.setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ // Medium network is fine for min chunk
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ // Medium downstream
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ // Medium upstream
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
// Fast network looks great
assertTrue(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
.setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
// Slow network still good given time
assertTrue(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
@@ -241,7 +279,6 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = new ConnectivityController(mService);
when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
-
// Suspended networks aren't usable.
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -299,8 +336,17 @@ public class ConnectivityControllerTest {
final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
+ job.setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, DataUnit.MEBIBYTES.toBytes(1));
+ final JobStatus latePrefetchUnknownDown = createJobStatus(job, now - 2000, now + 1000);
+ job.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), JobInfo.NETWORK_BYTES_UNKNOWN);
+ final JobStatus latePrefetchUnknownUp = createJobStatus(job, now - 2000, now + 1000);
+
final ConnectivityController controller = new ConnectivityController(mService);
+ when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+ .thenReturn(0L);
+
// Unmetered network is whenever
{
final Network net = mock(Network.class);
@@ -312,9 +358,11 @@ public class ConnectivityControllerTest {
assertTrue(controller.isSatisfied(late, net, caps, mConstants));
assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
}
- // Metered network is only when prefetching and late
+ // Metered network is only when prefetching, late, and in opportunistic quota
{
final Network net = mock(Network.class);
final NetworkCapabilities caps = createCapabilitiesBuilder()
@@ -323,7 +371,17 @@ public class ConnectivityControllerTest {
assertFalse(controller.isSatisfied(early, net, caps, mConstants));
assertFalse(controller.isSatisfied(late, net, caps, mConstants));
assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+ when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+ .thenReturn(9876543210L);
assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ // Only relax restrictions when we at least know the estimated download bytes.
+ assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index db0c3aece023..d4f17f39d144 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -21,10 +21,10 @@ import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.FallbackCategoryProvider
import android.content.pm.FeatureInfo
-import android.content.pm.PackageParser.SigningDetails
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
import android.content.pm.Signature
+import android.content.pm.SigningDetails
import android.content.pm.UserInfo
import android.content.pm.parsing.ParsingPackage
import android.content.pm.parsing.ParsingPackageUtils
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index f91cb2801bc1..ab43adc37c24 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -19,9 +19,11 @@ package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -123,9 +125,15 @@ public class StagingManagerTest {
mStagingManager.createSession(session2);
// Session1 should not fail in spite of the overlapping packages
mStagingManager.checkNonOverlappingWithStagedSessions(session1);
- // Session2 should fail due to overlapping packages
+ // setSessionFailed() should've been called when doing overlapping checks on session1
+ verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
+
+ // Yet another session with overlapping packages
+ StagingManager.StagedSession session3 = createSession(333, "com.foo", 3);
+ mStagingManager.createSession(session3);
assertThrows(PackageManagerException.class,
- () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
+ () -> mStagingManager.checkNonOverlappingWithStagedSessions(session3));
+ verify(session3, never()).setSessionFailed(anyInt(), anyString());
}
@Test
@@ -227,8 +235,8 @@ public class StagingManagerTest {
assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
mStagingManager.onBootCompletedBroadcastReceived();
- assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
- assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+ assertThat(nonReadyApkSession.hasVerificationStarted()).isTrue();
+ assertThat(nonReadyApexSession.hasVerificationStarted()).isTrue();
}
@Test
@@ -505,6 +513,7 @@ public class StagingManagerTest {
Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
return filter.test(stagedSession);
}).when(stagedSession).sessionContains(any());
+ doNothing().when(stagedSession).setSessionFailed(anyInt(), anyString());
return stagedSession;
}
@@ -521,7 +530,7 @@ public class StagingManagerTest {
private int mParentSessionId = -1;
private String mPackageName;
private boolean mIsAbandonded = false;
- private boolean mPreRebootVerificationStarted = false;
+ private boolean mVerificationStarted = false;
private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
private FakeStagedSession(int sessionId) {
@@ -552,8 +561,8 @@ public class StagingManagerTest {
return mIsAbandonded;
}
- private boolean hasPreRebootVerificationStarted() {
- return mPreRebootVerificationStarted;
+ private boolean hasVerificationStarted() {
+ return mVerificationStarted;
}
private FakeStagedSession addChildSession(FakeStagedSession session) {
@@ -708,20 +717,13 @@ public class StagingManagerTest {
}
@Override
- public boolean notifyStartPreRebootVerification() {
- mPreRebootVerificationStarted = true;
- // TODO(ioffe): change to true when tests for pre-reboot verification are added.
- return false;
- }
-
- @Override
public void notifyEndPreRebootVerification() {
throw new UnsupportedOperationException();
}
@Override
public void verifySession() {
- throw new UnsupportedOperationException();
+ mVerificationStarted = true;
}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
index 64b24c12f046..60a7f78c6949 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -95,18 +95,34 @@ public final class TestableDeviceConfig implements StaticMockFixture {
String name = invocationOnMock.getArgument(1);
String value = invocationOnMock.getArgument(2);
mKeyValueMap.put(getKey(namespace, name), value);
- for (DeviceConfig.OnPropertiesChangedListener listener :
- mOnPropertiesChangedListenerMap.keySet()) {
- if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
- mOnPropertiesChangedListenerMap.get(listener).second.execute(
- () -> listener.onPropertiesChanged(
- getProperties(namespace, name, value)));
- }
- }
+ invokeListeners(namespace, getProperties(namespace, name, value));
return true;
}
).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String namespace = invocationOnMock.getArgument(0);
+ String name = invocationOnMock.getArgument(1);
+ mKeyValueMap.remove(getKey(namespace, name));
+ invokeListeners(namespace, getProperties(namespace, name, null));
+ return true;
+ }
+ ).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ Properties properties = invocationOnMock.getArgument(0);
+ String namespace = properties.getNamespace();
+ Map<String, String> keyValues = new ArrayMap<>();
+ for (String name : properties.getKeyset()) {
+ String value = properties.getString(name, /* defaultValue= */ "");
+ mKeyValueMap.put(getKey(namespace, name), value);
+ keyValues.put(name.toLowerCase(), value);
+ }
+ invokeListeners(namespace, getProperties(namespace, keyValues));
+ return true;
+ }
+ ).when(() -> DeviceConfig.setProperties(any(Properties.class)));
+
doAnswer((Answer<String>) invocationOnMock -> {
String namespace = invocationOnMock.getArgument(0);
String name = invocationOnMock.getArgument(1);
@@ -153,6 +169,16 @@ public final class TestableDeviceConfig implements StaticMockFixture {
return Pair.create(values[0], values[1]);
}
+ private void invokeListeners(String namespace, Properties properties) {
+ for (DeviceConfig.OnPropertiesChangedListener listener :
+ mOnPropertiesChangedListenerMap.keySet()) {
+ if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
+ mOnPropertiesChangedListenerMap.get(listener).second.execute(
+ () -> listener.onPropertiesChanged(properties));
+ }
+ }
+ }
+
private Properties getProperties(String namespace, String name, String value) {
return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
index 0e40669cf870..f9f43876f39a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityThread;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.BadConfigException;
import android.provider.DeviceConfig.Properties;
import androidx.test.filters.SmallTest;
@@ -92,6 +93,30 @@ public class TestableDeviceConfigTest {
}
@Test
+ public void setProperties() throws BadConfigException {
+ String newKey = "key2";
+ String newValue = "value2";
+ DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
+ sValue).setString(newKey, newValue).build());
+ assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
+ assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
+ }
+
+ @Test
+ public void deleteProperty() {
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
+ DeviceConfig.deleteProperty(sNamespace, sKey);
+ assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
+ String newNamespace = "namespace2";
+ String newValue = "value2";
+ DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
+ assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
+ DeviceConfig.deleteProperty(newNamespace, sKey);
+ assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
+ }
+
+ @Test
public void getProperties_empty() {
String newKey = "key2";
String newValue = "value2";
@@ -131,13 +156,12 @@ public class TestableDeviceConfigTest {
}
@Test
- public void testListener() throws InterruptedException {
+ public void testListener_setProperty() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
OnPropertiesChangedListener changeListener = (properties) -> {
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset().size()).isEqualTo(1);
- assertThat(properties.getKeyset()).contains(sKey);
+ assertThat(properties.getKeyset()).containsExactly(sKey);
assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
countDownLatch.countDown();
@@ -153,6 +177,53 @@ public class TestableDeviceConfigTest {
}
}
+ @Test
+ public void testListener_setProperties() throws BadConfigException, InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ String newKey = "key2";
+ String newValue = "value2";
+
+ OnPropertiesChangedListener changeListener = (properties) -> {
+ assertThat(properties.getNamespace()).isEqualTo(sNamespace);
+ assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
+ assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
+ assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
+ assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
+ countDownLatch.countDown();
+ };
+ try {
+ DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+ ActivityThread.currentApplication().getMainExecutor(), changeListener);
+ DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
+ sValue).setString(newKey, newValue).build());
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } finally {
+ DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+ }
+ }
+
+ @Test
+ public void testListener_deleteProperty() throws InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ OnPropertiesChangedListener changeListener = (properties) -> {
+ assertThat(properties.getNamespace()).isEqualTo(sNamespace);
+ assertThat(properties.getKeyset()).containsExactly(sKey);
+ assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
+ assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
+ countDownLatch.countDown();
+ };
+ try {
+ DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+ ActivityThread.currentApplication().getMainExecutor(), changeListener);
+ DeviceConfig.deleteProperty(sNamespace, sKey);
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } finally {
+ DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+ }
+ }
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 339a5f916b4b..d1afa1a997bf 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -39,6 +39,7 @@ android_test {
"services.usage",
"services.uwb",
"guava",
+ "guava-android-testlib",
"androidx.test.core",
"androidx.test.ext.truth",
"androidx.test.runner",
@@ -117,6 +118,7 @@ android_test {
":PackageParserTestApp2",
":PackageParserTestApp3",
":PackageParserTestApp4",
+ ":PackageParserTestApp5",
":apex.test",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d6c11a549dfa..e612d121b093 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -65,6 +65,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index 80e81d6e7cb9..554f0a4265be 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -29,6 +29,7 @@ import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEA
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -176,7 +177,7 @@ public class AccessibilityInputFilterTest {
}
@Test
- public void testEventHandler_shouldChangeAfterOnDisplayChanged() {
+ public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() {
prepareLooper();
// Check if there is only one mEventHandler when there is one default display.
@@ -184,13 +185,51 @@ public class AccessibilityInputFilterTest {
assertEquals(1, mEventHandler.size());
// Check if it has correct numbers of mEventHandler for corresponding displays.
- setDisplayCount(4);
- mA11yInputFilter.onDisplayChanged();
- assertEquals(4, mEventHandler.size());
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+ assertEquals(2, mEventHandler.size());
+
+ EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY);
+ assertNotNull(next);
+
+ // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
+ // DEFAULT_DISPLAY.
+ for (int i = 1; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ next = next.getNext();
+ }
+ }
+
+ @Test
+ public void testEventHandler_shouldDecreaseAfterOnDisplayRemoved() {
+ prepareLooper();
setDisplayCount(2);
- mA11yInputFilter.onDisplayChanged();
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
assertEquals(2, mEventHandler.size());
+
+ // Check if it has correct numbers of mEventHandler for corresponding displays.
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ assertEquals(1, mEventHandler.size());
+
+ EventStreamTransformation eventHandler = mEventHandler.get(SECOND_DISPLAY);
+ assertNull(eventHandler);
+ }
+
+ @Test
+ public void testEventHandler_shouldNoChangedInOtherDisplayAfterOnDisplayRemoved() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ EventStreamTransformation eventHandlerBeforeDisplayRemoved =
+ mEventHandler.get(DEFAULT_DISPLAY);
+
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ EventStreamTransformation eventHandlerAfterDisplayRemoved =
+ mEventHandler.get(DEFAULT_DISPLAY);
+
+ assertEquals(eventHandlerBeforeDisplayRemoved, eventHandlerAfterDisplayRemoved);
}
@Test
@@ -240,7 +279,7 @@ public class AccessibilityInputFilterTest {
}
@Test
- public void testInputEvent_shouldClearEventsForAllEventHandlers() {
+ public void testInputEvent_shouldClearEventsForDisplayEventHandlers() {
prepareLooper();
mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
@@ -253,13 +292,71 @@ public class AccessibilityInputFilterTest {
send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
assertEquals(2, mCaptor1.mEvents.size());
- // InputEvent with different input source should trigger clearEvents() for each
- // EventStreamTransformation in EventHandler.
+ // InputEvent with different input source to the same display should trigger
+ // clearEvents() for the EventHandler in this display.
send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_MOUSE));
assertEquals(1, mCaptor1.mEvents.size());
}
@Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayEventHandlers() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(2, mEventHandler.size());
+
+ mCaptor1 = new EventCaptor();
+ mCaptor2 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+ mEventHandler.put(SECOND_DISPLAY, mCaptor2);
+
+ // InputEvent with different displayId should be dispatched to corresponding EventHandler.
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+
+ // InputEvent with different input source should not trigger clearEvents() for
+ // the EventHandler in the other display.
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_MOUSE));
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayAdded() {
+ prepareLooper();
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayRemoved() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationGestureHandler() {
prepareLooper();
doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW).when(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index ee00cb24a991..c5416fb02ad1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -595,7 +595,7 @@ public class AccessibilitySecurityPolicyTest {
serviceInfo.permission = android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE;
mA11ySecurityPolicy.canRegisterService(serviceInfo);
verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
- serviceInfo.applicationInfo.uid, serviceInfo.packageName);
+ serviceInfo.applicationInfo.uid, serviceInfo.packageName, null, null);
}
@Test
@@ -617,7 +617,7 @@ public class AccessibilitySecurityPolicyTest {
mA11ySecurityPolicy.checkAccessibilityAccess(mMockA11yServiceConnection);
verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- APP_UID, PACKAGE_NAME);
+ APP_UID, PACKAGE_NAME, null, null);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 00daa5c89fba..432a500a5041 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index e4d51e4374a7..4afe0996e12a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -120,6 +120,7 @@ public class AccessibilityWindowManagerTest {
@Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
@Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
@Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock private AccessibilityTraceManager mMockA11yTraceManager;
@Mock private IBinder mMockHostToken;
@Mock private IBinder mMockEmbeddedToken;
@@ -140,7 +141,8 @@ public class AccessibilityWindowManagerTest {
mMockWindowManagerInternal,
mMockA11yEventSender,
mMockA11ySecurityPolicy,
- mMockA11yUserManager);
+ mMockA11yUserManager,
+ mMockA11yTraceManager);
// Starts tracking window of default display and sets the default display
// as top focused display before each testing starts.
startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
@@ -834,6 +836,19 @@ public class AccessibilityWindowManagerTest {
assertNull(token);
}
+ @Test
+ public void onDisplayReparented_shouldRemoveObserver() throws RemoteException {
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
+ // Notifies the second display is an embedded one of the default display.
+ final WindowsForAccessibilityCallback callbacks =
+ mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
+ callbacks.onDisplayReparented(SECONDARY_DISPLAY_ID);
+ // Makes sure the observer of the second display is removed.
+ assertFalse(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
+ }
+
private void registerLeashedTokenAndWindowId() {
mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 78e651b7a3c1..c62cae5e9b6e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -56,11 +56,13 @@ public class KeyboardInterceptorTest {
private MessageCapturingHandler mHandler = new MessageCapturingHandler(
msg -> mInterceptor.handleMessage(msg));
@Mock AccessibilityManagerService mMockAms;
+ @Mock AccessibilityTraceManager mMockTraceManager;
@Mock WindowManagerPolicy mMockPolicy;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
mInterceptor = new KeyboardInterceptor(mMockAms, mMockPolicy, mHandler);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index d4908ee78a1d..59b69f9ffebf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -122,6 +122,7 @@ public class MotionEventInjectorTest {
MotionEventInjector mMotionEventInjector;
IAccessibilityServiceClient mServiceInterface;
+ AccessibilityTraceManager mTrace;
List<GestureStep> mLineList = new ArrayList<>();
List<GestureStep> mClickList = new ArrayList<>();
List<GestureStep> mContinuedLineList1 = new ArrayList<>();
@@ -148,7 +149,8 @@ public class MotionEventInjectorTest {
return mMotionEventInjector.handleMessage(msg);
}
});
- mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler);
+ mTrace = mock(AccessibilityTraceManager.class);
+ mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler, mTrace);
mServiceInterface = mock(IAccessibilityServiceClient.class);
mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 160308762a58..4ce9ba031b25 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.app.UiAutomation;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 7bf0bb873fc3..4a06611f397e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -36,6 +36,7 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_E
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
@@ -53,6 +54,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.utils.GestureLogParser;
import com.android.server.testutils.OffsettableClock;
@@ -107,6 +109,8 @@ public class TouchExplorerTest {
@Mock
private AccessibilityManagerService mMockAms;
+ @Mock
+ private AccessibilityTraceManager mMockTraceManager;
@Captor
private ArgumentCaptor<AccessibilityGestureEvent> mGestureCaptor;
@@ -143,9 +147,9 @@ public class TouchExplorerTest {
if (Looper.myLooper() == null) {
Looper.prepare();
}
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
mContext = InstrumentationRegistry.getContext();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
mCaptor = new EventCaptor();
mHandler = new TestHandler();
mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 502f64a1e50b..fe4fed9da468 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
@@ -93,6 +94,7 @@ public class FullScreenMagnificationControllerTest {
mock(FullScreenMagnificationController.ControllerContext.class);
final Context mMockContext = mock(Context.class);
final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
+ final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
private final MagnificationAnimationCallback mAnimationCallback = mock(
MagnificationAnimationCallback.class);
@@ -113,9 +115,11 @@ public class FullScreenMagnificationControllerTest {
when(mMockContext.getMainLooper()).thenReturn(looper);
when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
+ when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
initMockWindowManager();
mFullScreenMagnificationController = new FullScreenMagnificationController(
@@ -773,20 +777,20 @@ public class FullScreenMagnificationControllerTest {
}
@Test
- public void testRotation_resetsMagnification() {
+ public void testDisplaySizeChanged_resetsMagnification() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
- rotation_resetsMagnification(i);
+ changeDisplaySize_resetsMagnification(i);
resetMockWindowManager();
}
}
- private void rotation_resetsMagnification(int displayId) {
+ private void changeDisplaySize_resetsMagnification(int displayId) {
register(displayId);
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
- callbacks.onRotationChanged(0);
+ callbacks.onDisplaySizeChanged();
mMessageCapturingHandler.sendAllMessages();
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index f881f04e5b07..b14c353397e2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
@@ -129,6 +130,10 @@ public class FullScreenMagnificationGestureHandlerTest {
MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
@Mock
WindowMagnificationPromptController mWindowMagnificationPromptController;
+ @Mock
+ AccessibilityManagerService mMockAccessibilityManagerService;
+ @Mock
+ AccessibilityTraceManager mMockTraceManager;
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
@@ -144,7 +149,9 @@ public class FullScreenMagnificationGestureHandlerTest {
mock(FullScreenMagnificationController.ControllerContext.class);
final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
when(mockController.getContext()).thenReturn(mContext);
- when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
+ when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService);
+ when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager);
+ when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
when(mockController.getWindowManager()).thenReturn(mockWindowManager);
when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
@@ -179,7 +186,7 @@ public class FullScreenMagnificationGestureHandlerTest {
private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap,
boolean detectShortcutTrigger) {
FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
- mContext, mFullScreenMagnificationController, mMockCallback,
+ mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
mHandler = new TestHandler(h.mDetectingState, mClock) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index b7f5f4da9d9d..e82adc8b403b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -54,6 +54,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import org.junit.After;
import org.junit.Before;
@@ -84,6 +85,8 @@ public class MagnificationControllerTest {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ private AccessibilityTraceManager mTraceManager;
+ @Mock
private AccessibilityManagerService mService;
@Mock
private MagnificationController.TransitionCallBack mTransitionCallBack;
@@ -112,7 +115,7 @@ public class MagnificationControllerTest {
CURRENT_USER_ID);
mWindowMagnificationManager = Mockito.spy(
new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mock(WindowMagnificationManager.Callback.class)));
+ mock(WindowMagnificationManager.Callback.class), mTraceManager));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 514d16a0149c..ef6ed88011ef 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -31,6 +31,8 @@ import android.view.MotionEvent;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +51,8 @@ public class MagnificationGestureHandlerTest {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ AccessibilityTraceManager mTraceManager;
+ @Mock
MagnificationGestureHandler.Callback mCallback;
@Before
@@ -57,6 +61,7 @@ public class MagnificationGestureHandlerTest {
mMgh = new TestMagnificationGestureHandler(DISPLAY_0,
/* detectTripleTap= */true,
/* detectShortcutTrigger= */true,
+ mTraceManager,
mCallback);
}
@@ -129,8 +134,9 @@ public class MagnificationGestureHandlerTest {
boolean mIsInternalMethodCalled = false;
TestMagnificationGestureHandler(int displayId, boolean detectTripleTap,
- boolean detectShortcutTrigger, @NonNull Callback callback) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ boolean detectShortcutTrigger, @NonNull AccessibilityTraceManager trace,
+ @NonNull Callback callback) {
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index c88bc3b2e15b..1638563e8242 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -29,6 +29,8 @@ import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -45,6 +47,8 @@ public class WindowMagnificationConnectionWrapperTest {
private IWindowMagnificationConnection mConnection;
@Mock
+ private AccessibilityTraceManager mTrace;
+ @Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -57,7 +61,7 @@ public class WindowMagnificationConnectionWrapperTest {
MockitoAnnotations.initMocks(this);
mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
mConnection = mMockWindowMagnificationConnection.getConnection();
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection, mTrace);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index b9498d641ed7..6a5aae672881 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -35,6 +35,7 @@ import android.view.ViewConfiguration;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.utils.TouchEventGenerator;
@@ -74,16 +75,18 @@ public class WindowMagnificationGestureHandlerTest {
private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
@Mock
MagnificationGestureHandler.Callback mMockCallback;
+ @Mock
+ AccessibilityTraceManager mMockTrace;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
- mock(WindowMagnificationManager.Callback.class));
+ mock(WindowMagnificationManager.Callback.class), mMockTrace);
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mMockCallback,
+ mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
/** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a20272ab438d..af6d40f2fdf2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -52,6 +52,7 @@ import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
@@ -72,6 +73,8 @@ public class WindowMagnificationManagerTest {
@Mock
private Context mContext;
@Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -88,7 +91,7 @@ public class WindowMagnificationManagerTest {
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mMockCallback);
+ mMockCallback, mMockTrace);
when(mContext.getContentResolver()).thenReturn(mResolver);
doAnswer((InvocationOnMock invocation) -> {
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index 54945fbc2dd2..c0ba3c72beb4 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -75,7 +75,7 @@ public class AudioServiceTest {
for (boolean muted : new boolean[] { true, false}) {
testAudioSystem.configureIsMicrophoneMuted(!muted);
mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), null);
Assert.assertEquals("mic mute reporting wrong value",
muted, mAudioService.isMicrophoneMuted());
// verify the intent for mic mute changed is supposed to be fired
@@ -100,7 +100,7 @@ public class AudioServiceTest {
for (boolean muted : new boolean[] { true, false}) {
testAudioSystem.configureIsMicrophoneMuted(!muted);
mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), null);
Assert.assertEquals("mic mute reporting wrong value",
!muted, mAudioService.isMicrophoneMuted());
// verify the intent for mic mute changed is supposed to be fired
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 7323096121c4..7aea65e78616 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.backup.BackupManager.OperationType;
@@ -30,8 +29,8 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
@@ -41,7 +40,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -540,9 +538,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -558,9 +556,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -635,9 +633,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -656,9 +654,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -677,9 +675,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {signature1Copy, signature2Copy},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -698,9 +696,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -719,9 +717,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -743,9 +741,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -770,9 +768,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 5fcce671db1e..e2536f86b0e2 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -39,8 +39,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Bundle;
import android.os.Process;
@@ -376,9 +376,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -413,9 +413,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -451,9 +451,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -492,9 +492,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 2;
@@ -536,9 +536,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 1;
@@ -576,9 +576,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index a19b3872949e..363c26b63bae 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,9 +52,7 @@ import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -76,25 +73,29 @@ public class ColorDisplayServiceTest {
private int mUserId;
private MockTwilightManager mTwilightManager;
+ private DisplayTransformManager mDisplayTransformManager;
private ColorDisplayService mCds;
private ColorDisplayService.BinderService mBinderService;
private Resources mResourcesSpy;
- @BeforeClass
- public static void setDtm() {
- final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
- LocalServices.addService(DisplayTransformManager.class, dtm);
- }
+ private static final int[] MINIMAL_COLOR_MODES = new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL,
+ ColorDisplayManager.COLOR_MODE_BOOSTED,
+ };
@Before
public void setUp() {
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
doReturn(mContext).when(mContext).getApplicationContext();
- mResourcesSpy = Mockito.spy(mContext.getResources());
- when(mContext.getResources()).thenReturn(mResourcesSpy);
+ final Resources res = Mockito.spy(mContext.getResources());
+ doReturn(MINIMAL_COLOR_MODES).when(res).getIntArray(R.array.config_availableColorModes);
+ doReturn(true).when(res).getBoolean(R.bool.config_nightDisplayAvailable);
+ doReturn(true).when(res).getBoolean(R.bool.config_displayWhiteBalanceAvailable);
+ when(mContext.getResources()).thenReturn(res);
+ mResourcesSpy = res;
mUserId = ActivityManager.getCurrentUser();
@@ -108,6 +109,10 @@ public class ColorDisplayServiceTest {
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
+ mDisplayTransformManager = Mockito.mock(DisplayTransformManager.class);
+ doReturn(true).when(mDisplayTransformManager).needsLinearColorMatrix();
+ LocalServices.addService(DisplayTransformManager.class, mDisplayTransformManager);
+
mCds = new ColorDisplayService(mContext);
mBinderService = mCds.new BinderService();
LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -116,12 +121,18 @@ public class ColorDisplayServiceTest {
@After
public void tearDown() {
- LocalServices.removeServiceForTest(TwilightManager.class);
-
+ /*
+ * Wait for internal {@link Handler} to finish processing pending messages, so that test
+ * code can safelyremove {@link DisplayTransformManager} mock from {@link LocalServices}.
+ */
+ mCds.mHandler.runWithScissors(() -> { /* nop */ }, /* timeout */ 1000);
mCds = null;
+ LocalServices.removeServiceForTest(TwilightManager.class);
mTwilightManager = null;
+ LocalServices.removeServiceForTest(DisplayTransformManager.class);
+
mUserId = UserHandle.USER_NULL;
mContext = null;
@@ -130,11 +141,6 @@ public class ColorDisplayServiceTest {
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
- @AfterClass
- public static void removeDtm() {
- LocalServices.removeServiceForTest(DisplayTransformManager.class);
- }
-
@Test
public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
@@ -1064,24 +1070,18 @@ public class ColorDisplayServiceTest {
@Test
public void compositionColorSpaces_noResources() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {});
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
.thenReturn(new int[] {});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
}
@Test
public void compositionColorSpaces_invalidResources() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL,
@@ -1094,15 +1094,12 @@ public class ColorDisplayServiceTest {
});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
}
@Test
public void compositionColorSpaces_validResources_validColorMode() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1113,15 +1110,12 @@ public class ColorDisplayServiceTest {
});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_SRGB));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB));
}
@Test
public void compositionColorSpaces_validResources_invalidColorMode() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1132,8 +1126,8 @@ public class ColorDisplayServiceTest {
});
setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 6cc8d471f0f5..a38a84b80e81 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -45,6 +45,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ActiveSourceAction} */
@SmallTest
@@ -76,7 +77,7 @@ public class ActiveSourceActionTest {
mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 97bd066be543..5cf81ec2ccf8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -45,6 +45,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ArcInitiationActionFromAvrTest} */
@SmallTest
@@ -79,7 +80,7 @@ public class ArcInitiationActionFromAvrTest {
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
- new HdmiControlService(mContextSpy) {
+ new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
boolean isPowerStandby() {
return false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 29c9b407d609..1462839ff5a1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -45,6 +45,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ArcTerminationActionFromAvr} */
@SmallTest
@@ -80,7 +81,7 @@ public class ArcTerminationActionFromAvrTest {
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
- new HdmiControlService(mContextSpy) {
+ new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
void wakeUp() {
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 8b23be511ac0..f49b1c13087b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -34,6 +34,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
@SmallTest
@Presubmit
@@ -53,7 +55,8 @@ public class DetectTvSystemAudioModeSupportActionTest {
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
HdmiControlService hdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void sendCecCommand(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 650ffe9abd4b..fe8d691d6fb8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -51,6 +51,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link DevicePowerStatusAction} */
@SmallTest
@@ -88,7 +89,7 @@ public class DevicePowerStatusActionTest {
mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -220,6 +221,7 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
@@ -240,6 +242,7 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
@@ -263,6 +266,7 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index fa5cb67c573d..0d5d05c31bce 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -54,6 +54,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@RunWith(JUnit4.class)
@@ -105,7 +106,8 @@ public class DeviceSelectActionTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 41946fb8ec63..abc1468e53bf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -69,12 +69,27 @@ final class FakeHdmiCecConfig extends HdmiCecConfig {
R.bool.config_cecHdmiCecVersion20_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControl_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlEnabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlDisabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlMode_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlModeTv_allowed);
- doReturn(true).when(resources).getBoolean(
+ doReturn(false).when(resources).getBoolean(
R.bool.config_cecPowerControlModeTv_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlModeBroadcast_allowed);
doReturn(false).when(resources).getBoolean(
R.bool.config_cecPowerControlModeBroadcast_default);
@@ -95,6 +110,17 @@ final class FakeHdmiCecConfig extends HdmiCecConfig {
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControl_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecSystemAudioModeMuting_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 1958cb01b510..d630ef6a5cd3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -56,6 +56,7 @@ final class FakeNativeWrapper implements NativeWrapper {
private final Map<Integer, Boolean> mPortConnectionStatus = new HashMap<>();
private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
+ private int mVendorId = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
private HdmiCecController.HdmiCecCallback mCallback = null;
private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
@@ -102,7 +103,7 @@ final class FakeNativeWrapper implements NativeWrapper {
@Override
public int nativeGetVendorId() {
- return 0;
+ return mVendorId;
}
@Override
@@ -181,6 +182,11 @@ final class FakeNativeWrapper implements NativeWrapper {
}
@VisibleForTesting
+ protected void setVendorId(int vendorId) {
+ mVendorId = vendorId;
+ }
+
+ @VisibleForTesting
protected void setPhysicalAddress(int physicalAddress) {
mMyPhysicalAddress = physicalAddress;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 29f62b58746e..da10ec459508 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -61,6 +62,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/**
* Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework.
@@ -103,7 +105,7 @@ public class HdmiCecAtomLoggingTest {
mIThermalServiceMock, new Handler(mLooper)));
doReturn(true).when(mIPowerManagerMock).isInteractive();
- mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
@@ -200,7 +202,7 @@ public class HdmiCecAtomLoggingTest {
mTestLooper.dispatchAll();
- verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
+ verify(mHdmiCecAtomWriterSpy, atLeastOnce()).messageReported(
any(),
anyInt(),
eq(callerUid),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index a37f94b023fd..a94690e71eb4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -82,8 +82,10 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getAllSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -103,8 +105,10 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -125,7 +129,9 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -188,6 +194,7 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
HdmiControlManager.POWER_CONTROL_MODE_NONE);
}
@@ -199,6 +206,7 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@@ -255,12 +263,12 @@ public final class HdmiCecConfigTest {
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
- .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
}
@Test
public void getDefaultStringValue_WithOverride() {
- setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
@@ -277,7 +285,7 @@ public final class HdmiCecConfigTest {
@Test
public void getDefaultStringValue_NoDefault() {
- setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
assertThrows(RuntimeException.class,
() -> new HdmiCecConfig(mContext, mStorageAdapter));
}
@@ -334,7 +342,7 @@ public final class HdmiCecConfigTest {
public void getStringValue_GlobalSetting_BasicSanity() {
when(mStorageAdapter.retrieveGlobalSetting(
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.POWER_CONTROL_MODE_TV))
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM))
.thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getStringValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index ee1a85745701..bd6e46d9ee69 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -69,6 +69,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
/** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
@@ -99,7 +100,7 @@ public class HdmiCecControllerTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlServiceSpy = spy(new HdmiControlService(
- InstrumentationRegistry.getTargetContext()));
+ InstrumentationRegistry.getTargetContext(), Collections.emptyList()));
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getIoLooper();
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getServiceLooper();
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 7911c409d766..e03e1be4e107 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -53,6 +53,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@Presubmit
@@ -96,7 +97,8 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -210,7 +212,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
- mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true);
+ mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
new HdmiPortInfo(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 524ad6282397..99e3d683db5c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -53,6 +53,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -94,7 +95,8 @@ public class HdmiCecLocalDevicePlaybackTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void wakeUp() {
mWokenUp = true;
@@ -694,20 +696,45 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_ScreenOff_NotActiveSource_ToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -719,20 +746,20 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -744,20 +771,20 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -769,20 +796,45 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_ScreenOff_ActiveSource_ToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -794,20 +846,20 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -819,20 +871,20 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
}
@@ -844,9 +896,6 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -1456,7 +1505,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepToTv() {
+ public void oneTouchPlay_PowerControlModeToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_TV);
@@ -1479,7 +1528,30 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepBroadcast() {
+ public void oneTouchPlay_PowerControlModeToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(systemAudioModeRequest);
+ }
+
+ @Test
+ public void oneTouchPlay_PowerControlModeBroadcast() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -1502,7 +1574,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepNone() {
+ public void oneTouchPlay_PowerControlModeNone() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_NONE);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 6502e4813171..6c2db364aa4f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -18,10 +18,8 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
-import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
-import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID;
import static com.android.server.hdmi.Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -40,6 +38,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.Result;
import android.media.AudioManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -47,6 +46,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.google.common.testing.EqualsTester;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -141,7 +142,7 @@ public class HdmiCecLocalDeviceTest {
Context context = InstrumentationRegistry.getTargetContext();
mHdmiControlService =
- new HdmiControlService(context) {
+ new HdmiControlService(context, Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return isControlEnabled;
@@ -205,6 +206,25 @@ public class HdmiCecLocalDeviceTest {
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mNativeWrapper.setPhysicalAddress(0x2000);
mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ }
+
+ @Test
+ public void testEqualsActiveSource() {
+ int logicalAddress = 0;
+ int physicalAddress = 0x0000;
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress),
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress + 1))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress + 1, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(
+ logicalAddress + 1, physicalAddress + 1))
+ .testEquals();
}
@Test
@@ -221,37 +241,71 @@ public class HdmiCecLocalDeviceTest {
@Test
public void handleGivePhysicalAddress_success() {
- mSrcAddr = ADDR_UNREGISTERED;
- mDesAddr = ADDR_BROADCAST;
- param =
- new byte[] {
- (byte) ((mPhysicalAddr >> 8) & 0xFF),
- (byte) (mPhysicalAddr & 0xFF),
- (byte) (DEVICE_TV & 0xFF)
- };
- callbackResult = -1;
- @Constants.HandleMessageResult int handleResult =
+ mNativeWrapper.setPhysicalAddress(0x0);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_TV, 0, DEVICE_TV);
+ @Constants.HandleMessageResult
+ int handleResult =
mHdmiLocalDevice.handleGivePhysicalAddress(
- (int finalResult) -> callbackResult = finalResult);
+ HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ ADDR_PLAYBACK_1, ADDR_TV));
mTestLooper.dispatchAll();
- /**
- * Test if CecMessage is sent successfully SendMessageResult#SUCCESS is defined in HAL as 0
- */
- assertEquals(0, callbackResult);
assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleGivePhysicalAddress_failure() {
+ mNativeWrapper.setPhysicalAddress(Constants.INVALID_PHYSICAL_ADDRESS);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_TV,
+ ADDR_PLAYBACK_1,
+ Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS,
+ Constants.ABORT_UNABLE_TO_DETERMINE);
+ @Constants.HandleMessageResult
+ int handleResult =
+ mHdmiLocalDevice.handleGivePhysicalAddress(
+ HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ ADDR_PLAYBACK_1, ADDR_TV));
+ mTestLooper.dispatchAll();
+ assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@Test
public void handleGiveDeviceVendorId_success() {
- mSrcAddr = ADDR_UNREGISTERED;
- mDesAddr = ADDR_BROADCAST;
- /** nativeGetVendorId returns 0 */
- param = new byte[] {(byte) ((0 >> 8) & 0xFF), (byte) (0 & 0xFF), (byte) (0 & 0xFF)};
- callbackResult = -1;
- mHdmiLocalDevice.handleGiveDeviceVendorId(
- (int finalResult) -> callbackResult = finalResult);
+ /** Set vendor id to 0 */
+ mNativeWrapper.setVendorId(0);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(ADDR_TV, 0);
+ @Constants.HandleMessageResult
+ int handleResult =
+ mHdmiLocalDevice.handleGiveDeviceVendorId(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(
+ ADDR_PLAYBACK_1, ADDR_TV));
+ mTestLooper.dispatchAll();
+ assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleGiveDeviceVendorId_failure() {
+ mNativeWrapper.setVendorId(Result.FAILURE_UNKNOWN);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_TV,
+ ADDR_PLAYBACK_1,
+ Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID,
+ Constants.ABORT_UNABLE_TO_DETERMINE);
+ @Constants.HandleMessageResult
+ int handleResult =
+ mHdmiLocalDevice.handleGiveDeviceVendorId(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(
+ ADDR_PLAYBACK_1, ADDR_TV));
mTestLooper.dispatchAll();
- assertEquals(0, callbackResult);
+ assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 59711a62f3f3..5a2f7bb5c9e8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -56,6 +56,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@RunWith(JUnit4.class)
@@ -88,7 +89,8 @@ public class HdmiCecLocalDeviceTvTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void wakeUp() {
mWokenUp = true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 2307a85fe9cd..ebd8d77ff5cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -37,6 +37,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link com.android.server.hdmi.HdmiCecMessageValidator} class. */
@SmallTest
@Presubmit
@@ -49,7 +51,7 @@ public class HdmiCecMessageValidatorTest {
@Before
public void setUp() throws Exception {
HdmiControlService mHdmiControlService = new HdmiControlService(
- InstrumentationRegistry.getTargetContext());
+ InstrumentationRegistry.getTargetContext(), Collections.emptyList());
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index b1998f55c72e..e7d1b957bce7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -64,7 +64,7 @@ public class HdmiCecNetworkTest {
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
- mHdmiControlService = new HdmiControlService(mContext) {
+ mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList()) {
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListenerStatuses.add(status);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 572ffd930bad..dc633a2429d1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -47,6 +47,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@Presubmit
@@ -83,7 +84,7 @@ public class HdmiCecPowerStatusControllerTest {
mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(contextSpy) {
+ mHdmiControlService = new HdmiControlService(contextSpy, Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index f53ae525eaba..755eef3a578b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -63,6 +63,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Optional;
/**
@@ -205,7 +206,7 @@ public class HdmiControlServiceTest {
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
- mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index b820df3871ce..9466f5270085 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,8 +91,6 @@ public class OneTouchPlayActionTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
- setHdmiControlEnabled(hdmiControlEnabled);
-
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
new PowerManager(mContextSpy, mIPowerManagerMock,
mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
@@ -137,6 +135,7 @@ public class OneTouchPlayActionTest {
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
+ setHdmiControlEnabled(hdmiControlEnabled);
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
new file mode 100644
index 000000000000..bd307fdf6e50
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
+import static com.android.server.hdmi.Constants.MESSAGE_ACTIVE_SOURCE;
+import static com.android.server.hdmi.Constants.MESSAGE_ROUTING_INFORMATION;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.RoutingControlAction.STATE_WAIT_FOR_ROUTING_INFORMATION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class RoutingControlActionTest {
+ /*
+ * Example connection diagram used in tests. Double-lined paths indicate the currently active
+ * routes.
+ *
+ *
+ * +-----------+
+ * | TV |
+ * | 0.0.0.0 |
+ * +---+-----+-+
+ * | |
+ * <----------+ 1) AVR -> Switch
+ * +----------+ | | +-----------+
+ * | AVR +---------+ +--+ Switch |
+ * | 1.0.0.0 | | 2.0.0.0 |
+ * +--+---++--+ +--++-----+-+ <-------+ 2) Recorder -> Blu-ray
+ * | || || |
+ * | || || +--------+
+ * +-----------+ | || +----------+ +----++----+ |
+ * | XBox +--+ ++--+ Tuner | | Blueray | +-----+----+
+ * | 1.1.0.0 | | 1.2.0.0 | | 2.1.0.0 | | Recorder |
+ * +-----------+ +----++----+ +----------+ | 2.2.0.0 |
+ * || +----------+
+ * ||
+ * +----++----+
+ * | Player |
+ * | 1.2.1.0 |
+ * +----------+
+ *
+ */
+
+ private static final int PHYSICAL_ADDRESS_TV = 0x0000;
+ private static final int PHYSICAL_ADDRESS_AVR = 0x1000;
+ private static final int PHYSICAL_ADDRESS_SWITCH = 0x2000;
+ private static final int PHYSICAL_ADDRESS_TUNER = 0x1200;
+ private static final int PHYSICAL_ADDRESS_PLAYER = 0x1210;
+ private static final int PHYSICAL_ADDRESS_BLUERAY = 0x2100;
+ private static final int PHYSICAL_ADDRESS_RECORDER = 0x2200;
+ private static final int PORT_1 = 1;
+ private static final int PORT_2 = 2;
+ private static final int VENDOR_ID_AVR = 0x11233;
+
+ private static final byte[] TUNER_PARAM =
+ new byte[] {(PHYSICAL_ADDRESS_TUNER >> 8) & 0xFF, PHYSICAL_ADDRESS_TUNER & 0xFF};
+ private static final byte[] PLAYER_PARAM =
+ new byte[] {(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF};
+
+ private static final HdmiDeviceInfo DEVICE_INFO_AVR =
+ new HdmiDeviceInfo(ADDR_AUDIO_SYSTEM, PHYSICAL_ADDRESS_AVR, PORT_1,
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, VENDOR_ID_AVR, "Audio");
+ private static final HdmiDeviceInfo DEVICE_INFO_PLAYER =
+ new HdmiDeviceInfo(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYER, PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK, VENDOR_ID_AVR, "Player");
+ private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = new HdmiCecMessage(
+ ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM);
+ private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = new HdmiCecMessage(
+ ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM);
+ private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = new HdmiCecMessage(
+ ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM);
+ private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = new HdmiCecMessage(
+ ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM);
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mMyLooper = mTestLooper.getLooper();
+ PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
+
+ HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
+
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ boolean isPowerStandbyOrTransient() {
+ return false;
+ }
+
+ @Override
+ protected PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ protected HdmiCecConfig getHdmiCecConfig() {
+ return hdmiCecConfig;
+ }
+ };
+
+ mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mHdmiCecLocalDeviceTv.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDeviceTv);
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR,
+ true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mHdmiControlService.initService();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+ }
+
+ private static class TestActionTimer implements ActionTimer {
+ private int mState;
+
+ @Override
+ public void sendTimerMessage(int state, long delayMillis) {
+ mState = state;
+ }
+
+ @Override
+ public void clearTimerMessage() {
+ }
+
+ private int getState() {
+ return mState;
+ }
+ }
+
+ private static class TestInputSelectCallback extends IHdmiControlCallback.Stub {
+ private final List<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assert (mCallbackResult.size() == 1);
+ return mCallbackResult.get(0);
+ }
+ }
+
+ private static RoutingControlAction createRoutingControlAction(HdmiCecLocalDeviceTv localDevice,
+ TestInputSelectCallback callback) {
+ return new RoutingControlAction(localDevice, PHYSICAL_ADDRESS_AVR, callback);
+ }
+
+ // Routing control succeeds against the device connected directly to the port. Action
+ // won't get any <Routing Information> in this case. It times out on <Routing Information>,
+ // regards the directly connected one as the new routing path to switch to.
+ @Test
+ public void testRoutingControl_succeedForDirectlyConnectedDevice() {
+ TestInputSelectCallback callback = new TestInputSelectCallback();
+ TestActionTimer actionTimer = new TestActionTimer();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+
+ RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+ action.setActionTimer(actionTimer);
+ action.start();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+ action.handleTimerEvent(actionTimer.getState());
+ mTestLooper.dispatchAll();
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_TV, PHYSICAL_ADDRESS_AVR);
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+
+ // Succeeds by receiving a couple of <Routing Information> commands, followed by
+ // <Set Stream Path> going out in the end.
+ @Test
+ public void testRoutingControl_succeedForDeviceBehindSwitch() {
+ TestInputSelectCallback callback = new TestInputSelectCallback();
+ TestActionTimer actionTimer = new TestActionTimer();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_PLAYER);
+ RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+ action.setActionTimer(actionTimer);
+ action.start();
+
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+ action.processCommand(ROUTING_INFORMATION_TUNER);
+ action.processCommand(ROUTING_INFORMATION_PLAYER);
+
+ action.handleTimerEvent(actionTimer.getState());
+ mTestLooper.dispatchAll();
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_TV, PHYSICAL_ADDRESS_PLAYER);
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 2cf4ef1f14cb..290e4b001aa4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -48,6 +48,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/**
* Test for {@link SystemAudioAutoInitiationAction}.
@@ -86,7 +87,7 @@ public class SystemAudioAutoInitiationActionTest {
mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index e82c788020ed..2019a1651c5f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -38,6 +38,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link SystemAudioInitiationActionFromAvr} */
@SmallTest
@Presubmit
@@ -65,7 +67,8 @@ public class SystemAudioInitiationActionFromAvrTest {
Context context = InstrumentationRegistry.getTargetContext();
- HdmiControlService hdmiControlService = new HdmiControlService(context) {
+ HdmiControlService hdmiControlService = new HdmiControlService(context,
+ Collections.emptyList()) {
@Override
void sendCecCommand(
HdmiCecMessage command, @Nullable SendMessageCallback callback) {
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
deleted file mode 100644
index 9ab762aaa8fa..000000000000
--- a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.inputmethod;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeTrue;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MultiClientInputMethodManagerServiceTest {
-
- @Before
- public void setUp() {
- // MultiClientInputMethodManagerService is only testable if build is debuggable.
- assumeTrue(Build.IS_DEBUGGABLE);
- }
-
- @Test
- public void testQueryInputMethod_noIMEFound() {
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- emptyList())).isNull();
- }
-
- @Test
- public void testQueryInputMethod_multipleIMEsFound() {
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(new ResolveInfo(), new ResolveInfo()))).isNull();
- }
-
- @Test
- public void testQueryInputMethod_IMEFound_invalidPermission() {
- // Arrange
- ResolveInfo imeService = buildResolveInfo(/* permission= */ "",
- ApplicationInfo.FLAG_SYSTEM);
-
- // Act and assert
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(imeService))).isNull();
- }
-
- @Test
- public void testQueryInputMethod_IMEFound() {
- // Arrange
- ResolveInfo imeService = buildResolveInfo(android.Manifest.permission.BIND_INPUT_METHOD,
- ApplicationInfo.FLAG_SYSTEM);
-
- // Act and assert
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(imeService))).isSameInstanceAs(imeService);
- }
-
- private ResolveInfo buildResolveInfo(String permission, int flags) {
- ResolveInfo imeService = new ResolveInfo();
- imeService.serviceInfo = new ServiceInfo();
- imeService.serviceInfo.packageName = "com.android.server.inputmethod";
- imeService.serviceInfo.name = "someIMEService";
- imeService.serviceInfo.permission = permission;
- imeService.serviceInfo.applicationInfo = new ApplicationInfo();
- imeService.serviceInfo.applicationInfo.flags = flags;
- return imeService;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 67dd0556e098..be942d6738af 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -30,9 +30,8 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
@@ -252,8 +251,8 @@ public class AppsFilterTest {
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final ParsingPackage android = pkg("android");
watcher.verifyNoChangeReported("prepare");
android.addProtectedBroadcast("TEST_ACTION");
@@ -592,12 +591,12 @@ public class AppsFilterTest {
appsFilter.onSystemReady();
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final Signature otherSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails otherSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{otherSignature}, 1);
+ final SigningDetails otherSigningDetails =
+ new SigningDetails(new Signature[]{otherSignature}, 1);
simulateAddPackage(appsFilter, pkg("android"), 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
@@ -1199,8 +1198,8 @@ public class AppsFilterTest {
private void simulateAddBasicAndroid(AppsFilter appsFilter) throws Exception {
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final ParsingPackage android = pkg("android");
simulateAddPackage(appsFilter, android, 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index c572dd65245f..4f77afb4969f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -77,12 +77,12 @@ import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -1413,9 +1413,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
pi.applicationInfo.setVersionCode(version);
pi.signatures = null;
pi.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
genSignatures(signatures),
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
return pi;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 128cbaaffd74..a710839fca76 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,6 +15,10 @@
*/
package com.android.server.pm;
+import static android.content.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,6 +29,7 @@ import static org.junit.Assert.fail;
import static java.lang.Boolean.TRUE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static java.util.stream.Collectors.toList;
import android.annotation.NonNull;
import android.content.Context;
@@ -35,10 +40,10 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedComponent;
@@ -83,6 +88,7 @@ import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -106,6 +112,7 @@ public class PackageParserTest {
private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
+ private static final String TEST_APP5_APK = "PackageParserTestApp5.apk";
private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
@Before
@@ -506,6 +513,56 @@ public class PackageParserTest {
}
}
+ @Test
+ public void testParseModernPackageHasNoCompatPermissions() throws Exception {
+ final File testFile = extractFile(TEST_APP1_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2()
+ .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+ final List<String> compatPermissions =
+ Arrays.stream(COMPAT_PERMS).map(p -> p.name).collect(toList());
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into uses permissions.")
+ .that(pkg.getUsesPermissions().stream().map(p -> p.name).collect(toList()))
+ .containsNoneIn(compatPermissions);
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into requested permissions.")
+ .that(pkg.getRequestedPermissions()).containsNoneIn(compatPermissions);
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into implicit permissions.")
+ .that(pkg.getImplicitPermissions()).containsNoneIn(compatPermissions);
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseLegacyPackageHasCompatPermissions() throws Exception {
+ final File testFile = extractFile(TEST_APP5_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2()
+ .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+ assertWithMessage(
+ "Compatibility permissions should be added into uses permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
+ .allMatch(pkg.getUsesPermissions().stream().map(p -> p.name)
+ .collect(toList())::contains))
+ .isTrue();
+ assertWithMessage(
+ "Compatibility permissions should be added into requested permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
+ .allMatch(pkg.getRequestedPermissions()::contains))
+ .isTrue();
+ assertWithMessage(
+ "Compatibility permissions should be added into implicit permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
+ .allMatch(pkg.getImplicitPermissions()::contains))
+ .isTrue();
+ } finally {
+ testFile.delete();
+ }
+ }
+
/**
* A trivial subclass of package parser that only caches the package name, and throws away
* all other information.
@@ -645,7 +702,8 @@ public class PackageParserTest {
assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
assertEquals(a.getVersionName(), b.getVersionName());
assertEquals(a.getSharedUserId(), b.getSharedUserId());
- assertArrayEquals(a.getSigningDetails().signatures, b.getSigningDetails().signatures);
+ assertArrayEquals(a.getSigningDetails().getSignatures(),
+ b.getSigningDetails().getSignatures());
assertEquals(a.getRestrictedAccountType(), b.getRestrictedAccountType());
assertEquals(a.getRequiredAccountType(), b.getRequiredAccountType());
assertEquals(a.getOverlayTarget(), b.getOverlayTarget());
@@ -653,7 +711,7 @@ public class PackageParserTest {
assertEquals(a.getOverlayCategory(), b.getOverlayCategory());
assertEquals(a.getOverlayPriority(), b.getOverlayPriority());
assertEquals(a.isOverlayIsStatic(), b.isOverlayIsStatic());
- assertEquals(a.getSigningDetails().publicKeys, b.getSigningDetails().publicKeys);
+ assertEquals(a.getSigningDetails().getPublicKeys(), b.getSigningDetails().getPublicKeys());
assertEquals(a.getUpgradeKeySets(), b.getUpgradeKeySets());
assertEquals(a.getKeySetMapping(), b.getKeySetMapping());
assertArrayEquals(a.getRestrictUpdateHash(), b.getRestrictUpdateHash());
@@ -873,9 +931,9 @@ public class PackageParserTest {
.setVersionName("foo17")
.setSharedUserId("foo18")
.setSigningDetails(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[]{new Signature(new byte[16])},
- 2,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
new ArraySet<>(),
null)
)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f75751bf54ae..f551ad15f72e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -16,8 +16,8 @@
package com.android.server.pm;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
+import android.content.pm.SigningDetails;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -47,7 +47,7 @@ public class PackageSettingBuilder {
private String[] mUsesStaticLibraries;
private long[] mUsesStaticLibrariesVersions;
private Map<String, ArraySet<String>> mMimeGroups;
- private PackageParser.SigningDetails mSigningDetails;
+ private SigningDetails mSigningDetails;
private UUID mDomainSetId = UUID.randomUUID();
public PackageSettingBuilder setPackage(AndroidPackage pkg) {
@@ -159,7 +159,7 @@ public class PackageSettingBuilder {
}
public PackageSettingBuilder setSigningDetails(
- PackageParser.SigningDetails signingDetails) {
+ SigningDetails signingDetails) {
mSigningDetails = signingDetails;
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 27f3eec655e3..b9431bf86d65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -22,11 +22,9 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.util.TypedXmlPullParser;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -39,7 +37,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
-import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
@@ -107,10 +104,10 @@ public class PackageSignaturesTest {
}
private static final int[] CAPABILITIES =
- {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+ {SigningDetails.CertCapabilities.INSTALLED_DATA,
+ SigningDetails.CertCapabilities.SHARED_USER_ID,
+ SigningDetails.CertCapabilities.PERMISSION,
+ SigningDetails.CertCapabilities.ROLLBACK};
@Before
public void setUp() throws Exception {
@@ -173,7 +170,7 @@ public class PackageSignaturesTest {
assertEquals(
"The signing details was not UNKNOWN after parsing an invalid public key cert key"
+ " attribute",
- PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+ SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
}
@Test
@@ -181,14 +178,14 @@ public class PackageSignaturesTest {
// Verifies if the sigs count attribute is missing then the signature cannot be read but the
// method does not throw an exception.
verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+ SigningDetails.SignatureSchemeVersion.UNKNOWN);
}
@Test
public void testReadXmlWithMissingSchemeVersion() throws Exception {
// Verifies if the schemeVersion is an invalid value the signature can still be obtained.
verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
FIRST_EXPECTED_SIGNATURE);
}
@@ -198,7 +195,7 @@ public class PackageSignaturesTest {
// obtained.
verifyReadXmlReturnsExpectedSignaturesAndLineage(
"xml/three-signers-in-lineage-missing-scheme-version.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
}
@@ -386,7 +383,7 @@ public class PackageSignaturesTest {
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value",
expectedSchemeVersion,
- mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
}
/**
@@ -402,7 +399,7 @@ public class PackageSignaturesTest {
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value", schemeVersion,
- mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
for (Signature signature : signatures) {
String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index f1930d7268d7..901b200417c9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class SELinuxMMACTest {
private static final String PACKAGE_NAME = "my.package";
- private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+ private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
@Mock
@@ -91,6 +91,16 @@ public class SELinuxMMACTest {
}
@Test
+ public void getSeInfoTargetingCurDevelopment() {
+ AndroidPackage pkg = makePackage(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + Build.VERSION_CODES.CUR_DEVELOPMENT));
+ }
+
+ @Test
public void getSeInfoNoOptInButAlreadyR() {
AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 182760b30005..b447857a05af 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -25,8 +25,8 @@ import static org.mockito.Mockito.mock;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.platform.test.annotations.Presubmit;
import android.test.MoreAsserts;
@@ -95,9 +95,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -114,9 +114,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -197,9 +197,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -219,9 +219,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -240,9 +240,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -262,9 +262,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -285,9 +285,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -309,9 +309,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -336,9 +336,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5eabc1bea148..b43161408b1c 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -741,6 +741,33 @@ public class PowerManagerServiceTest {
}
@Test
+ public void testSuspendBlockerHeldDuringBoot() throws Exception {
+ final String suspendBlockerName = "PowerManagerService.Booting";
+
+ final boolean[] isAcquired = new boolean[1];
+ doAnswer(inv -> {
+ isAcquired[0] = false;
+ return null;
+ }).when(mNativeWrapperMock).nativeReleaseSuspendBlocker(eq(suspendBlockerName));
+
+ doAnswer(inv -> {
+ isAcquired[0] = true;
+ return null;
+ }).when(mNativeWrapperMock).nativeAcquireSuspendBlocker(eq(suspendBlockerName));
+
+ // Need to create the service after we stub the mocks for this test because some of the
+ // mocks are used during the constructor.
+ createService();
+ assertTrue(isAcquired[0]);
+
+ mService.systemReady(null);
+ assertTrue(isAcquired[0]);
+
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ assertFalse(isAcquired[0]);
+ }
+
+ @Test
public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception {
setMinimumScreenOffTimeoutConfig(5);
setAttentiveWarningDuration(120);
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 4618157ef56a..2a4c3fdf8f43 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -32,6 +32,7 @@ import static org.mockito.MockitoAnnotations.initMocks;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
+import android.util.SparseIntArray;
import com.android.server.pm.ApexManager;
import com.android.server.pm.Installer;
@@ -119,8 +120,9 @@ public class AppDataRollbackHelperTest {
}
private static Rollback createRollbackForId(int rollbackId) {
- return new Rollback(rollbackId, new File("/does/not/exist"), -1,
- 0, "com.xyz");
+ return new Rollback(rollbackId, new File("/does/not/exist"), -1, /* isStaged */ false, 0,
+ "com.xyz", null, new SparseIntArray(0));
+
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index c42f936d3ab4..9d56a36196bb 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -82,7 +82,7 @@ public class RollbackStoreTest {
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
- + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer'}";
@@ -102,7 +102,7 @@ public class RollbackStoreTest {
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
- + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer',"
+ "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
@@ -129,12 +129,13 @@ public class RollbackStoreTest {
SparseIntArray extensionVersions = new SparseIntArray();
extensionVersions.put(30, 71);
Rollback rollback = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
assertThat(rollback.isStaged()).isFalse();
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
assertThat(rollback.isEnabling()).isTrue();
@@ -153,7 +154,7 @@ public class RollbackStoreTest {
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
assertThat(rollback.isStaged()).isTrue();
- assertThat(rollback.getStagedSessionId()).isEqualTo(897);
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(897);
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
@@ -168,7 +169,7 @@ public class RollbackStoreTest {
extensionVersions.put(5, 25);
extensionVersions.put(30, 71);
Rollback origRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
origRb.setRestoreUserDataInProgress(true);
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -218,7 +219,7 @@ public class RollbackStoreTest {
@Test
public void loadFromJsonNoExtensionVersions() throws Exception {
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -268,7 +269,7 @@ public class RollbackStoreTest {
extensionVersions.put(5, 25);
extensionVersions.put(30, 71);
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -315,7 +316,7 @@ public class RollbackStoreTest {
@Test
public void saveAndDelete() {
Rollback rollback = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
RollbackStore.saveRollback(rollback);
@@ -331,7 +332,7 @@ public class RollbackStoreTest {
@Test
public void saveToHistoryAndLoad() {
Rollback origRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
mRollbackStore.saveRollbackToHistory(origRb);
List<Rollback> loadedRollbacks = mRollbackStore.loadHistorialRollbacks();
@@ -364,7 +365,7 @@ public class RollbackStoreTest {
assertThat(b.getApexPackageNames())
.containsExactlyElementsIn(a.getApexPackageNames());
- assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId());
+ assertThat(b.getOriginalSessionId()).isEqualTo(a.getOriginalSessionId());
assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId());
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index cf1ed4815a74..5ba4851270fd 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -74,18 +74,28 @@ public class RollbackUnitTest {
when(mMockPmi.getPackageList()).thenReturn(mPackageList);
}
+ private Rollback createStagedRollback(int rollbackId, File backupDir, int originalSessionId) {
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, USER,
+ INSTALLER, null, new SparseIntArray(0));
+ }
+
+ private Rollback createNonStagedRollback(int rollbackId, File backupDir) {
+ return new Rollback(rollbackId, backupDir, -1, /* isStaged */ false, USER,
+ INSTALLER, null, new SparseIntArray(0));
+ }
+
@Test
public void newEmptyStagedRollbackDefaults() {
int rollbackId = 123;
int sessionId = 567;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, sessionId, USER, INSTALLER);
+ Rollback rollback = createStagedRollback(rollbackId, file, sessionId);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
assertThat(rollback.isStaged()).isTrue();
- assertThat(rollback.getStagedSessionId()).isEqualTo(567);
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
}
@Test
@@ -93,7 +103,7 @@ public class RollbackUnitTest {
int rollbackId = 123;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(rollbackId, file);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
@@ -102,8 +112,7 @@ public class RollbackUnitTest {
@Test
public void rollbackMadeAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER,
- INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.isAvailable()).isFalse();
@@ -121,7 +130,7 @@ public class RollbackUnitTest {
@Test
public void deletedRollbackCannotBeMadeAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
rollback.delete(mMockDataHelper, "test");
@@ -135,7 +144,7 @@ public class RollbackUnitTest {
@Test
public void getPackageNamesAllAndJustApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 11, true);
PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 19, 1, false);
@@ -149,7 +158,7 @@ public class RollbackUnitTest {
@Test
public void includesPackagesAfterEnable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 157, 156, false);
@@ -177,7 +186,7 @@ public class RollbackUnitTest {
@Test
public void snapshotWhenEnabling() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -195,7 +204,7 @@ public class RollbackUnitTest {
@Test
public void snapshotWhenAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -216,7 +225,7 @@ public class RollbackUnitTest {
@Test
public void snapshotWhenDeleted() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -237,7 +246,7 @@ public class RollbackUnitTest {
@Test
public void snapshotThenDeleteNoApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, false);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -259,7 +268,7 @@ public class RollbackUnitTest {
@Test
public void snapshotThenDeleteWithApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -282,7 +291,7 @@ public class RollbackUnitTest {
@Test
public void restoreUserDataDoesNothingIfNotInProgress() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -297,7 +306,7 @@ public class RollbackUnitTest {
@Test
public void restoreUserDataDoesNothingIfPackageNotFound() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -313,7 +322,7 @@ public class RollbackUnitTest {
@Test
public void restoreUserDataRestoresIfInProgressAndPackageFound() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -329,20 +338,9 @@ public class RollbackUnitTest {
}
@Test
- public void notifySessionWithSuccess() {
- int[] sessionIds = new int[]{ 7777, 8888 };
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
- sessionIds, new SparseIntArray(0));
- // The 1st invocation returns false because not all child sessions are notified.
- assertThat(rollback.notifySessionWithSuccess()).isFalse();
- // The 2nd invocation returns true because now all child sessions are notified.
- assertThat(rollback.notifySessionWithSuccess()).isTrue();
- }
-
- @Test
public void allPackagesEnabled() {
int[] sessionIds = new int[]{ 7777, 8888 };
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, false, USER, INSTALLER,
sessionIds, new SparseIntArray(0));
// #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
diff --git a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
index fa2123c0d09a..03ccf8ccdd54 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -76,7 +76,8 @@ public class RotationResolverManagerPerUserServiceTest {
// setup a spy for the RotationResolverManagerPerUserService.
final RotationResolverManagerService mainService = new RotationResolverManagerService(
mContext);
- mService = new RotationResolverManagerPerUserService(mainService, /* Lock */ new Object(),
+ final Object lock = new Object();
+ mService = new RotationResolverManagerPerUserService(mainService, lock,
mContext.getUserId());
mCancellationSignal = new CancellationSignal();
@@ -84,15 +85,13 @@ public class RotationResolverManagerPerUserServiceTest {
mRequest = new RotationResolutionRequest("", Surface.ROTATION_0, Surface.ROTATION_0,
true, 1000L);
this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
- mMockCallbackInternal, mRequest, mCancellationSignal);
+ mMockCallbackInternal, mRequest, mCancellationSignal, lock);
this.mService.getMaster().mIsServiceEnabled = true;
ComponentName componentName = new ComponentName(PACKAGE_NAME, CLASS_NAME);
this.mService.mRemoteService = new MockRemoteRotationResolverService(mContext,
- componentName, mContext.getUserId(),
- /* idleUnbindTimeoutMs */60000L,
- /* Lock */ new Object());
+ componentName, mContext.getUserId(), /* idleUnbindTimeoutMs */60000L);
}
@Test
@@ -126,13 +125,13 @@ public class RotationResolverManagerPerUserServiceTest {
}
static class MockRemoteRotationResolverService extends RemoteRotationResolverService {
- MockRemoteRotationResolverService(Context context, ComponentName serviceName,
- int userId, long idleUnbindTimeoutMs, Object lock) {
- super(context, serviceName, userId, idleUnbindTimeoutMs, lock);
+ MockRemoteRotationResolverService(Context context, ComponentName serviceName, int userId,
+ long idleUnbindTimeoutMs) {
+ super(context, serviceName, userId, idleUnbindTimeoutMs);
}
@Override
- public void resolveRotationLocked(RotationRequest request) {
+ public void resolveRotation(RotationRequest request) {
request.mCallbackInternal.onSuccess(request.mRemoteRequest.getProposedRotation());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
new file mode 100644
index 000000000000..19474812104a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.Status;
+import android.os.HwParcel;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IHwInterface;
+import android.os.RemoteException;
+import android.system.OsConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.LinkedList;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class SoundHw2CompatTest {
+ @Parameterized.Parameter(0) public String mVersion;
+ @Parameterized.Parameter(1) public boolean mSupportConcurrentCapture;
+
+ private final Runnable mRebootRunnable = mock(Runnable.class);
+ private ISoundTriggerHal mCanonical;
+ private CaptureStateNotifier mCaptureStateNotifier;
+ private android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+
+ // We run the test once for every version of the underlying driver.
+ @Parameterized.Parameters(name = "{0}, concurrent={1}")
+ public static Iterable<Object[]> data() {
+ List<Object[]> result = new LinkedList<>();
+
+ for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3", "V2_4",}) {
+ for (boolean concurrentCapture : new boolean[]{false, true}) {
+ result.add(new Object[]{version, concurrentCapture});
+ }
+ }
+
+ return result;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mHalDriver = (android.hardware.soundtrigger.V2_0.ISoundTriggerHw) mock(Class.forName(
+ String.format("android.hardware.soundtrigger.%s.ISoundTriggerHw", mVersion)));
+
+ clearInvocations(mRebootRunnable);
+
+ // This binder is associated with the mock, so it can be cast to either version of the
+ // HAL interface.
+ final IHwBinder binder = new IHwBinder() {
+ @Override
+ public void transact(int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException {
+ // This is a little hacky, but a very easy way to gracefully reject a request for
+ // an unsupported interface (after queryLocalInterface() returns null, the client
+ // will attempt a remote transaction to obtain the interface. RemoteException will
+ // cause it to give up).
+ throw new RemoteException();
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
+ || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.4::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return mHalDriver;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+ try {
+ return mHalDriver.linkToDeath(recipient, cookie);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient) {
+ try {
+ return mHalDriver.unlinkToDeath(recipient);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ };
+ when(mHalDriver.asBinder()).thenReturn(binder);
+
+ android.hardware.soundtrigger.V2_3.Properties halProperties =
+ TestUtil.createDefaultProperties_2_3(mSupportConcurrentCapture);
+ doAnswer(invocation -> {
+ ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
+ 0)).onValues(0, halProperties.base);
+ return null;
+ }).when(mHalDriver).getProperties(any());
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ doAnswer(invocation -> {
+ ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback) invocation.getArgument(
+ 0)).onValues(0, halProperties);
+ return null;
+ }).when(driver).getProperties_2_3(any());
+ }
+
+ mCaptureStateNotifier = spy(new CaptureStateNotifier());
+
+ mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable,
+ mCaptureStateNotifier);
+
+ // During initialization any method can be called, but after we're starting to enforce that
+ // no additional methods are called.
+ clearInvocations(mHalDriver);
+ }
+
+ @After
+ public void tearDown() {
+ mCanonical.detach();
+ verifyNoMoreInteractions(mHalDriver);
+ verifyNoMoreInteractions(mRebootRunnable);
+ mCaptureStateNotifier.verifyNoMoreListeners();
+ }
+
+ @Test
+ public void testSetUpAndTearDown() {
+ }
+
+ @Test
+ public void testReboot() {
+ mCanonical.reboot();
+ verify(mRebootRunnable).run();
+ }
+
+ @Test
+ public void testGetProperties() throws Exception {
+ Properties properties = mCanonical.getProperties();
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ // It is OK for the SUT to cache the properties, so the underlying method doesn't
+ // need to be called every single time.
+ verify(driver, atMost(1)).getProperties_2_3(any());
+ TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture);
+ } else {
+ // It is OK for the SUT to cache the properties, so the underlying method doesn't
+ // need to be called every single time.
+ verify(mHalDriver, atMost(1)).getProperties(any());
+ TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture, 0, "");
+ }
+ }
+
+ private int loadGenericModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(), anyInt(),
+ any());
+
+ TestUtil.validateGenericSoundModel_2_0(modelCaptor.getValue());
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_1).loadSoundModel_2_1(any(), any(), anyInt(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(driver_2_4).loadSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+ any());
+
+ TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return loadGenericModel_2_4(canonicalCallback);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ return loadGenericModel_2_1(canonicalCallback);
+ } else {
+ return loadGenericModel_2_0(canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testLoadGenericModel() throws Exception {
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ loadGenericModel(canonicalCallback);
+ }
+
+ @Test
+ public void testMaxModels() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw);
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int maxModels = TestUtil.createDefaultProperties_2_0(false).maxSoundModels;
+ int[] modelHandles = new int[maxModels];
+
+ // Load as many models as we're allowed.
+ for (int i = 0; i < maxModels; ++i) {
+ modelHandles[i] = loadGenericModel(canonicalCallback);
+ verifyNoMoreInteractions(mHalDriver);
+ clearInvocations(mHalDriver);
+ }
+
+ // Now try to load an additional one and expect failure without invoking the underlying
+ // driver.
+ try {
+ mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ // Unload a single model and expect a onResourcesAvailable().
+ mCanonical.unloadSoundModel(modelHandles[0]);
+ verify(mHalDriver).unloadSoundModel(modelHandles[0]);
+
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ private void testLoadGenericModelBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(-OsConstants.EBUSY, 0);
+ return null;
+ }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ try {
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+ }
+
+ @Test
+ public void testLoadGenericModelBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testLoadGenericModelBusy_2_4();
+ }
+ }
+
+ private int loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validatePhraseSoundModel_2_0(modelCaptor.getValue());
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_1).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadPhraseModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(driver_2_4).loadPhraseSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+ any());
+
+ TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ public int loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return loadPhraseModel_2_4(canonicalCallback);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ return loadPhraseModel_2_1(canonicalCallback);
+ } else {
+ return loadPhraseModel_2_0(canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testLoadPhraseModel() throws Exception {
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ loadPhraseModel(canonicalCallback);
+ }
+
+ private void testLoadPhraseModelBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(-OsConstants.EBUSY, 0);
+ return null;
+ }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ try {
+ mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+ }
+
+ @Test
+ public void testLoadPhraseModelBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testLoadPhraseModelBusy_2_4();
+ }
+ }
+
+ @Test
+ public void testUnloadModel() throws Exception {
+ mCanonical.unloadSoundModel(14);
+ verify(mHalDriver).unloadSoundModel(14);
+ }
+
+ private void startRecognition_2_0(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
+ configCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ when(mHalDriver.startRecognition(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 203, 204, config);
+ verify(mHalDriver).startRecognition(eq(handle), configCaptor.capture(),
+ callbackCaptor.capture(), anyInt());
+
+ TestUtil.validateRecognitionConfig_2_0(configCaptor.getValue(), 203, 204);
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ private void startRecognition_2_1(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
+ configCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ when(driver_2_1.startRecognition_2_1(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 505, 506, config);
+ verify(driver_2_1).startRecognition_2_1(eq(handle), configCaptor.capture(),
+ callbackCaptor.capture(), anyInt());
+
+ TestUtil.validateRecognitionConfig_2_1(configCaptor.getValue(), 505, 506);
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ private void startRecognition_2_3(int handle) throws Exception {
+ final android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+ ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+ when(driver_2_3.startRecognition_2_3(eq(handle), any())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 808, 909, config);
+ verify(driver_2_3).startRecognition_2_3(eq(handle), configCaptor.capture());
+ TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 808, 909);
+ }
+
+ private void startRecognition_2_4(int handle) throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+ ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+ when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 21, 22, config);
+ verify(driver_2_4).startRecognition_2_4(eq(handle), configCaptor.capture());
+ TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 21, 22);
+ }
+
+ private void startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ startRecognition_2_4(handle);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ startRecognition_2_3(handle);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ startRecognition_2_1(handle, canonicalCallback);
+ } else {
+ startRecognition_2_0(handle, canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testStartRecognition() throws Exception {
+ // First load.
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Then start.
+ startRecognition(handle, canonicalCallback);
+ }
+
+ private void testStartRecognitionBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 68;
+ when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(-OsConstants.EBUSY);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ try {
+ mCanonical.startRecognition(handle, 34, 35, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).startRecognition_2_4(eq(handle), any());
+ }
+
+ @Test
+ public void testStartRecognitionBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testStartRecognitionBusy_2_4();
+ }
+ }
+
+ @Test
+ public void testNoRegisterCaptureStateListener() {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, never()).registerListener(any());
+ }
+
+ @Test
+ public void testConcurrentCaptureAbort() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ // Load.
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Then start.
+ startRecognition(handle, canonicalCallback);
+
+ // Now activate external capture.
+ mCaptureStateNotifier.setState(true);
+
+ // Expect hardware to have been stopped.
+ verify(mHalDriver).stopRecognition(handle);
+
+ // Expect an abort event (async).
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+
+ // Deactivate external capture.
+ mCaptureStateNotifier.setState(false);
+
+ // Expect a onResourcesAvailable().
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ @Test
+ public void testConcurrentCaptureReject() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ // Load (this registers the callback).
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Report external capture active.
+ mCaptureStateNotifier.setState(true);
+
+ // Then start.
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ try {
+ mCanonical.startRecognition(handle, 203, 204, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ // Deactivate external capture.
+ mCaptureStateNotifier.setState(false);
+
+ // Expect a onResourcesAvailable().
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ @Test
+ public void testStopRecognition() throws Exception {
+ mCanonical.stopRecognition(17);
+ verify(mHalDriver).stopRecognition(17);
+ }
+
+ @Test
+ public void testForceRecognition() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver_2_2 =
+ (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
+ mCanonical.forceRecognitionEvent(14);
+ verify(driver_2_2).getModelState(14);
+ } else {
+ try {
+ mCanonical.forceRecognitionEvent(14);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
+ }
+ }
+
+ @Test
+ public void testGetParameter() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback resultCallback =
+ invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, 99);
+ return null;
+ }).when(driver_2_3).getParameter(eq(21), eq(47), any());
+
+ assertEquals(99, mCanonical.getModelParameter(21, 47));
+ verify(driver_2_3).getParameter(eq(21), eq(47), any());
+ }
+
+ @Test
+ public void testSetParameter() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ mCanonical.setModelParameter(212, 247, 80);
+ verify(driver_2_3).setParameter(212, 247, 80);
+ }
+
+ @Test
+ public void testQueryParameterSupported() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ android.hardware.soundtrigger.V2_3.ModelParameterRange range =
+ new android.hardware.soundtrigger.V2_3.ModelParameterRange();
+ range.start = 34;
+ range.end = 45;
+ android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+ new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+ optionalRange.range(range);
+ resultCallback.onValues(0, optionalRange);
+ return null;
+ }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNotNull(range);
+ assertEquals(34, range.minInclusive);
+ assertEquals(45, range.maxInclusive);
+ verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+ }
+
+ @Test
+ public void testQueryParameterNotSupported() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+ new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+ resultCallback.onValues(0, optionalRange);
+ return null;
+ }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNull(range);
+ verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+ } else {
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNull(range);
+ }
+ }
+
+ private void testGlobalCallback_2_0() {
+ ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(canonicalCallback);
+ // We just care that it doesn't throw.
+ }
+
+ private void testGlobalCallback_2_4() throws Exception {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(canonicalCallback);
+
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback>
+ callbackCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.class);
+ verify(driver_2_4).registerGlobalCallback(callbackCaptor.capture());
+ validateGlobalCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ @Test
+ public void testGlobalCallback() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testGlobalCallback_2_4();
+ } else {
+ testGlobalCallback_2_0();
+ }
+ }
+
+ @Test
+ public void testLinkToDeath() throws Exception {
+ IBinder.DeathRecipient canonicalRecipient = mock(IBinder.DeathRecipient.class);
+ when(mHalDriver.linkToDeath(any(), anyLong())).thenReturn(true);
+ mCanonical.linkToDeath(canonicalRecipient);
+
+ ArgumentCaptor<IHwBinder.DeathRecipient> recipientCaptor = ArgumentCaptor.forClass(
+ IHwBinder.DeathRecipient.class);
+ ArgumentCaptor<Long> cookieCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mHalDriver).linkToDeath(recipientCaptor.capture(), cookieCaptor.capture());
+
+ recipientCaptor.getValue().serviceDied(cookieCaptor.getValue());
+ mCanonical.flushCallbacks();
+ verify(canonicalRecipient).binderDied();
+
+ mCanonical.unlinkToDeath(canonicalRecipient);
+ verify(mHalDriver).unlinkToDeath(recipientCaptor.getValue());
+ }
+
+ @Test
+ public void testInterfaceDescriptor() throws Exception {
+ when(mHalDriver.interfaceDescriptor()).thenReturn("ABCD");
+ assertEquals("ABCD", mCanonical.interfaceDescriptor());
+ verify(mHalDriver).interfaceDescriptor();
+ }
+
+ private void validateGlobalCallback_2_4(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback hwCallback,
+ ISoundTriggerHal.GlobalCallback canonicalCallback) throws Exception {
+ hwCallback.onResourcesAvailable();
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).onResourcesAvailable();
+ }
+
+ private void validateCallback_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback(TestUtil.createRecognitionEvent_2_0(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback(
+ TestUtil.createPhraseRecognitionEvent_2_0(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ private void validateCallback_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+ 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback_2_1(
+ TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ private void validateCallback_2_4(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+ 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback_2_1(
+ TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+
+ {
+ final int handle = 23;
+ hwCallback.modelUnloaded(handle);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).modelUnloaded(handle);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ public static class CaptureStateNotifier implements ICaptureStateNotifier {
+ private final List<Listener> mListeners = new LinkedList<>();
+
+ @Override
+ public boolean registerListener(Listener listener) {
+ mListeners.add(listener);
+ return false;
+ }
+
+ @Override
+ public void unregisterListener(Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void setState(boolean state) {
+ for (Listener listener : mListeners) {
+ listener.onCaptureStateChange(state);
+ }
+ }
+
+ public void verifyNoMoreListeners() {
+ assertEquals(0, mListeners.size());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 509eb2563376..1daf8317c541 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -16,89 +16,50 @@
package com.android.server.soundtrigger_middleware;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.audio.common.V2_0.AudioConfig;
-import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
-import android.media.audio.common.AudioChannelMask;
-import android.media.audio.common.AudioFormat;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.os.HidlMemoryUtil;
-import android.os.HwParcel;
-import android.os.IHwBinder;
-import android.os.IHwInterface;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.SharedMemory;
-import android.system.ErrnoException;
import android.util.Pair;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-import java.io.FileDescriptor;
-import java.nio.ByteBuffer;
-
-@RunWith(Parameterized.class)
+@RunWith(JUnit4.class)
public class SoundTriggerMiddlewareImplTest {
- private static final String TAG = "SoundTriggerMiddlewareImplTest";
-
- // We run the test once for every version of the underlying driver.
- @Parameterized.Parameters
- public static Object[] data() {
- return new Object[]{
- mock(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_2.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_3.ISoundTriggerHw.class),
- };
- }
-
- @Mock
- @Parameterized.Parameter
- public android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+ @Mock public ISoundTriggerHal mHalDriver = mock(ISoundTriggerHal.class);
- @Mock
- private SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider = mock(
- SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
+ @Mock private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider =
+ mock(SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
private SoundTriggerMiddlewareImpl mService;
@@ -106,522 +67,41 @@ public class SoundTriggerMiddlewareImplTest {
return mock(ISoundTriggerCallback.Stub.class, Mockito.CALLS_REAL_METHODS);
}
- private static SoundModel createGenericSoundModel() {
- return createSoundModel(SoundModelType.GENERIC);
- }
-
- private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
- try {
- SharedMemory shmem = SharedMemory.create("", data.length);
- ByteBuffer buffer = shmem.mapReadWrite();
- buffer.put(data);
- return shmem.getFileDescriptor();
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static SoundModel createSoundModel(int type) {
- SoundModel model = new SoundModel();
- model.type = type;
- model.uuid = "12345678-2345-3456-4567-abcdef987654";
- model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
- byte[] data = new byte[]{91, 92, 93, 94, 95};
- model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
- model.dataSize = data.length;
- return model;
- }
-
- private static PhraseSoundModel createPhraseSoundModel() {
- PhraseSoundModel model = new PhraseSoundModel();
- model.common = createSoundModel(SoundModelType.KEYPHRASE);
- model.phrases = new Phrase[1];
- model.phrases[0] = new Phrase();
- model.phrases[0].id = 123;
- model.phrases[0].users = new int[]{5, 6, 7};
- model.phrases[0].locale = "locale";
- model.phrases[0].text = "text";
- model.phrases[0].recognitionModes =
- RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
- return model;
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties(
- boolean supportConcurrentCapture) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
- properties.implementor = "implementor";
- properties.description = "description";
- properties.version = 123;
- properties.uuid = new Uuid();
- properties.uuid.timeLow = 1;
- properties.uuid.timeMid = 2;
- properties.uuid.versionAndTimeHigh = 3;
- properties.uuid.variantAndClockSeqHigh = 4;
- properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
-
- properties.maxSoundModels = 456;
- properties.maxKeyPhrases = 567;
- properties.maxUsers = 678;
- properties.recognitionModes =
- android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- properties.captureTransition = true;
- properties.maxBufferMs = 321;
- properties.concurrentCapture = supportConcurrentCapture;
- properties.triggerInEvent = true;
- properties.powerConsumptionMw = 432;
- return properties;
- }
-
- private static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
- boolean supportConcurrentCapture) {
- android.hardware.soundtrigger.V2_3.Properties properties =
- new android.hardware.soundtrigger.V2_3.Properties();
- properties.base = createDefaultProperties(supportConcurrentCapture);
- properties.supportedModelArch = "supportedModelArch";
- properties.audioCapabilities =
- android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
- | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
- return properties;
- }
-
- private void validateDefaultProperties(SoundTriggerModuleProperties properties,
- boolean supportConcurrentCapture) {
- assertEquals("implementor", properties.implementor);
- assertEquals("description", properties.description);
- assertEquals(123, properties.version);
- assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
- assertEquals(456, properties.maxSoundModels);
- assertEquals(567, properties.maxKeyPhrases);
- assertEquals(678, properties.maxUsers);
- assertEquals(RecognitionMode.GENERIC_TRIGGER
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
- assertTrue(properties.captureTransition);
- assertEquals(321, properties.maxBufferMs);
- assertEquals(supportConcurrentCapture, properties.concurrentCapture);
- assertTrue(properties.triggerInEvent);
- assertEquals(432, properties.powerConsumptionMw);
-
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- assertEquals("supportedModelArch", properties.supportedModelArch);
- assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
- properties.audioCapabilities);
- } else {
- assertEquals("", properties.supportedModelArch);
- assertEquals(0, properties.audioCapabilities);
- }
- }
-
- private void verifyNotGetProperties() throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
- never()).getProperties(any());
- }
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
- int hwHandle,
- int status) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
- halEvent.status = status;
- halEvent.type = SoundModelType.GENERIC;
- halEvent.model = hwHandle;
- halEvent.captureAvailable = true;
- // This field is ignored.
- halEvent.captureSession = 123;
- halEvent.captureDelayMs = 234;
- halEvent.capturePreambleMs = 345;
- halEvent.triggerInData = true;
- halEvent.audioConfig = new AudioConfig();
- halEvent.audioConfig.sampleRateHz = 456;
- halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
- halEvent.audioConfig.format = AudioFormat.MP3;
- // hwEvent.audioConfig.offloadInfo is irrelevant.
- halEvent.data.add((byte) 31);
- halEvent.data.add((byte) 32);
- halEvent.data.add((byte) 33);
- return halEvent;
- }
-
- private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
- int hwHandle,
- int status) {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
- halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
- halEvent.header.data.clear();
- halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
- return halEvent;
- }
-
- private static void validateRecognitionEvent(RecognitionEvent event, int status) {
- assertEquals(status, event.status);
- assertEquals(SoundModelType.GENERIC, event.type);
- assertTrue(event.captureAvailable);
- assertEquals(101, event.captureSession);
- assertEquals(234, event.captureDelayMs);
- assertEquals(345, event.capturePreambleMs);
- assertTrue(event.triggerInData);
- assertEquals(456, event.audioConfig.sampleRateHz);
- assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
- assertEquals(AudioFormat.MP3, event.audioConfig.format);
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
- int hwHandle, int status) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
- halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
-
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
- new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
- halExtra.id = 123;
- halExtra.confidenceLevel = 52;
- halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
- new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
- halLevel.userId = 31;
- halLevel.levelPercent = 43;
- halExtra.levels.add(halLevel);
- halEvent.phraseExtras.add(halExtra);
- return halEvent;
- }
-
- private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
- int hwHandle, int status) {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
- halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
-
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
- new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
- halExtra.id = 123;
- halExtra.confidenceLevel = 52;
- halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
- new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
- halLevel.userId = 31;
- halLevel.levelPercent = 43;
- halExtra.levels.add(halLevel);
- halEvent.phraseExtras.add(halExtra);
- return halEvent;
- }
-
- private static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event, int status) {
- validateRecognitionEvent(event.common, status);
-
- assertEquals(1, event.phraseExtras.length);
- assertEquals(123, event.phraseExtras[0].id);
- assertEquals(52, event.phraseExtras[0].confidenceLevel);
- assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
- event.phraseExtras[0].recognitionModes);
- assertEquals(1, event.phraseExtras[0].levels.length);
- assertEquals(31, event.phraseExtras[0].levels[0].userId);
- assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
- }
-
- private void initService(boolean supportConcurrentCapture) throws RemoteException {
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
- createDefaultProperties(
- supportConcurrentCapture);
- ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
- 0)).onValues(0,
- properties);
- return null;
- }).when(mHalDriver).getProperties(any());
-
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.Properties properties =
- createDefaultProperties_2_3(
- supportConcurrentCapture);
- ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback)
- invocation.getArgument(
- 0)).onValues(0,
- properties);
- return null;
- }).when(driver).getProperties_2_3(any());
- }
-
- mService = new SoundTriggerMiddlewareImpl(() -> {
- return mHalDriver;
- }, mAudioSessionProvider);
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- SoundModel model = createGenericSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
- invocation.getArgument(1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
- resultCallback = invocation.getArgument(3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.model = hwHandle;
- callback.soundModelCallback(modelEvent, callbackCookie);
- return null;
- }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadModel(model);
- verify(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel hidlModel =
- modelCaptor.getValue();
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
- hidlModel.type);
- assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.uuid));
- assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
- assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_1(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
- SoundModel model = createGenericSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
- invocation.getArgument(1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
- resultCallback = invocation.getArgument(3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.header.status =
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.header.model = hwHandle;
- callback.soundModelCallback_2_1(modelEvent, callbackCookie);
- return null;
- }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadModel(model);
- verify(driver).loadSoundModel_2_1(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel hidlModel =
- modelCaptor.getValue();
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
- hidlModel.header.type);
- assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.uuid));
- assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.vendorUuid));
- assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
- HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
private Pair<Integer, SoundTriggerHwCallback> loadGenericModel(ISoundTriggerModule module,
int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- return loadGenericModel_2_1(module, hwHandle);
- } else {
- return loadGenericModel_2_0(module, hwHandle);
- }
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_0(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- PhraseSoundModel model = createPhraseSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
- invocation.getArgument(
- 1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
- resultCallback =
- invocation.getArgument(
- 3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.model = hwHandle;
- callback.soundModelCallback(modelEvent, callbackCookie);
- return null;
- }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
+ SoundModel model = TestUtil.createGenericSoundModel();
+ ArgumentCaptor<SoundModel> modelCaptor = ArgumentCaptor.forClass(SoundModel.class);
+ ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+ ISoundTriggerHal.ModelCallback.class);
+ when(mHalDriver.loadSoundModel(any(), any())).thenReturn(hwHandle);
when(mAudioSessionProvider.acquireSession()).thenReturn(
new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
- int handle = module.loadPhraseModel(model);
- verify(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+ int handle = module.loadModel(model);
+ verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture());
verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel hidlModel =
- modelCaptor.getValue();
-
- // Validate common part.
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
- hidlModel.common.type);
- assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.uuid));
- assertEquals(model.common.vendorUuid,
- ConversionUtil.hidl2aidlUuid(hidlModel.common.vendorUuid));
- assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.common.data.toArray());
-
- // Validate phrase part.
- assertEquals(1, hidlModel.phrases.size());
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase hidlPhrase =
- hidlModel.phrases.get(0);
- assertEquals(123, hidlPhrase.id);
- assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
- assertEquals("locale", hidlPhrase.locale);
- assertEquals("text", hidlPhrase.text);
- assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
- hidlPhrase.recognitionModes);
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
+ assertEquals(model, modelCaptor.getValue());
+ return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
}
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_1(ISoundTriggerModule module,
+ private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(ISoundTriggerModule module,
int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
- PhraseSoundModel model = createPhraseSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
- invocation.getArgument(
- 1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
- resultCallback =
- invocation.getArgument(
- 3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.header.status =
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.header.model = hwHandle;
- callback.soundModelCallback_2_1(modelEvent, callbackCookie);
- return null;
- }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
+ PhraseSoundModel model = TestUtil.createPhraseSoundModel();
+ ArgumentCaptor<PhraseSoundModel> modelCaptor = ArgumentCaptor.forClass(
+ PhraseSoundModel.class);
+ ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+ ISoundTriggerHal.ModelCallback.class);
+ when(mHalDriver.loadPhraseSoundModel(any(), any())).thenReturn(hwHandle);
when(mAudioSessionProvider.acquireSession()).thenReturn(
new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
int handle = module.loadPhraseModel(model);
- verify(driver).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+ verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture());
verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
- modelCaptor.getValue();
-
- // Validate common part.
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
- hidlModel.common.header.type);
- assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.header.uuid));
- assertEquals(model.common.vendorUuid,
- ConversionUtil.hidl2aidlUuid(hidlModel.common.header.vendorUuid));
- assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
- HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.common.data));
-
- // Validate phrase part.
- assertEquals(1, hidlModel.phrases.size());
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Phrase hidlPhrase =
- hidlModel.phrases.get(0);
- assertEquals(123, hidlPhrase.id);
- assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
- assertEquals("locale", hidlPhrase.locale);
- assertEquals("text", hidlPhrase.text);
- assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
- hidlPhrase.recognitionModes);
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(
- ISoundTriggerModule module, int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- return loadPhraseModel_2_1(module, hwHandle);
- } else {
- return loadPhraseModel_2_0(module, hwHandle);
- }
+ assertEquals(model, modelCaptor.getValue());
+ return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
}
private void unloadModel(ISoundTriggerModule module, int handle, int hwHandle)
@@ -631,204 +111,35 @@ public class SoundTriggerMiddlewareImplTest {
verify(mAudioSessionProvider).releaseSession(101);
}
- private void startRecognition_2_0(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
-
- when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
- .thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
-
- module.startRecognition(handle, config);
- verify(mHalDriver).startRecognition(eq(hwHandle), any(), any(), anyInt());
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig halConfig =
- configCaptor.getValue();
- assertTrue(halConfig.captureRequested);
- assertEquals(102, halConfig.captureHandle);
- assertEquals(103, halConfig.captureDevice);
- assertEquals(1, halConfig.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
- }
-
- private void startRecognition_2_1(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
-
- when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
- .thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
-
- module.startRecognition(handle, config);
- verify(driver).startRecognition_2_1(eq(hwHandle), any(), any(), anyInt());
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig =
- configCaptor.getValue();
- assertTrue(halConfig.header.captureRequested);
- assertEquals(102, halConfig.header.captureHandle);
- assertEquals(103, halConfig.header.captureDevice);
- assertEquals(1, halConfig.header.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig.header.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
- HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
- }
-
- private void startRecognition_2_3(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
-
- when(driver.startRecognition_2_3(eq(hwHandle), configCaptor.capture())).thenReturn(0);
+ private void startRecognition(ISoundTriggerModule module, int handle, int hwHandle)
+ throws RemoteException {
+ ArgumentCaptor<RecognitionConfig> configCaptor = ArgumentCaptor.forClass(
+ RecognitionConfig.class);
- RecognitionConfig config = createRecognitionConfig();
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
module.startRecognition(handle, config);
- verify(driver).startRecognition_2_3(eq(hwHandle), any());
-
- android.hardware.soundtrigger.V2_3.RecognitionConfig halConfigExtended =
- configCaptor.getValue();
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig_2_1 =
- halConfigExtended.base;
-
- assertTrue(halConfig_2_1.header.captureRequested);
- assertEquals(102, halConfig_2_1.header.captureHandle);
- assertEquals(103, halConfig_2_1.header.captureDevice);
- assertEquals(1, halConfig_2_1.header.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig_2_1.header.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
- HidlMemoryUtil.hidlMemoryToByteArray(halConfig_2_1.data));
- assertEquals(AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION, halConfigExtended.audioCapabilities);
- }
-
- private void startRecognition(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- startRecognition_2_3(module, handle, hwHandle);
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- startRecognition_2_1(module, handle, hwHandle);
- } else {
- startRecognition_2_0(module, handle, hwHandle);
- }
- }
-
- private RecognitionConfig createRecognitionConfig() {
- RecognitionConfig config = new RecognitionConfig();
- config.captureRequested = true;
- config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
- config.phraseRecognitionExtras[0].id = 123;
- config.phraseRecognitionExtras[0].confidenceLevel = 4;
- config.phraseRecognitionExtras[0].recognitionModes = 5;
- config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
- config.phraseRecognitionExtras[0].levels[0].userId = 234;
- config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
- config.data = new byte[]{5, 4, 3, 2, 1};
- config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
- return config;
+ verify(mHalDriver).startRecognition(eq(hwHandle), eq(103), eq(102), configCaptor.capture());
+ assertEquals(config, configCaptor.getValue());
}
private void stopRecognition(ISoundTriggerModule module, int handle, int hwHandle)
throws RemoteException {
- when(mHalDriver.stopRecognition(hwHandle)).thenReturn(0);
module.stopRecognition(handle);
verify(mHalDriver).stopRecognition(hwHandle);
}
- private void verifyNotStartRecognition() throws RemoteException {
- verify(mHalDriver, never()).startRecognition(anyInt(), any(), any(), anyInt());
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
- never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
- never()).startRecognition_2_3(anyInt(), any());
- }
- }
-
-
@Before
public void setUp() throws Exception {
clearInvocations(mHalDriver);
clearInvocations(mAudioSessionProvider);
+ when(mHalDriver.getProperties()).thenReturn(TestUtil.createDefaultProperties(false));
+ mService = new SoundTriggerMiddlewareImpl(() -> mHalDriver, mAudioSessionProvider);
+ }
- // This binder is associated with the mock, so it can be cast to either version of the
- // HAL interface.
- final IHwBinder binder = new IHwBinder() {
- @Override
- public void transact(int code, HwParcel request, HwParcel reply, int flags)
- throws RemoteException {
- // This is a little hacky, but a very easy way to gracefully reject a request for
- // an unsupported interface (after queryLocalInterface() returns null, the client
- // will attempt a remote transaction to obtain the interface. RemoteException will
- // cause it to give up).
- throw new RemoteException();
- }
-
- @Override
- public IHwInterface queryLocalInterface(String descriptor) {
- if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
- || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- return mHalDriver;
- }
- return null;
- }
-
- @Override
- public boolean linkToDeath(DeathRecipient recipient, long cookie) {
- return true;
- }
-
- @Override
- public boolean unlinkToDeath(DeathRecipient recipient) {
- return true;
- }
- };
-
- when(mHalDriver.asBinder()).thenReturn(binder);
+ @After
+ public void tearDown() {
+ verify(mHalDriver, never()).reboot();
}
@Test
@@ -836,70 +147,58 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testListModules() throws Exception {
- initService(true);
+ public void testListModules() {
// Note: input and output properties are NOT the same type, even though they are in any way
// equivalent. One is a type that's exposed by the HAL and one is a type that's exposed by
// the service. The service actually performs a (trivial) conversion between the two.
SoundTriggerModuleDescriptor[] allDescriptors = mService.listModules();
assertEquals(1, allDescriptors.length);
- SoundTriggerModuleProperties properties = allDescriptors[0].properties;
-
- validateDefaultProperties(properties, true);
- verifyNotGetProperties();
+ Properties properties = allDescriptors[0].properties;
+ assertEquals(TestUtil.createDefaultProperties(false), properties);
}
@Test
public void testAttachDetach() throws Exception {
// Normal attachment / detachment.
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
assertNotNull(module);
module.detach();
}
@Test
- public void testAttachDetachNotAvailable() throws Exception {
- // Attachment / detachment during external capture, with a module not supporting concurrent
- // capture.
- initService(false);
+ public void testLoadUnloadModel() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(false);
- assertNotNull(module);
- module.detach();
- }
- @Test
- public void testAttachDetachAvailable() throws Exception {
- // Attachment / detachment during external capture, with a module supporting concurrent
- // capture.
- initService(true);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- assertNotNull(module);
+ final int hwHandle = 7;
+ int handle = loadGenericModel(module, hwHandle).first;
+ unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
- public void testLoadUnloadModel() throws Exception {
- initService(true);
+ public void testLoadPreemptModel() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 7;
- int handle = loadGenericModel(module, hwHandle).first;
- unloadModel(module, handle, hwHandle);
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
+
+ // Signal preemption.
+ hwCallback.sendUnloadEvent(hwHandle);
+
+ verify(callback).onModelUnloaded(handle);
+
module.detach();
}
@Test
public void testLoadUnloadPhraseModel() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -911,7 +210,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testStartStopRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -931,8 +229,31 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
+ public void testStartRecognitionBusy() throws Exception {
+ ISoundTriggerCallback callback = createCallbackMock();
+ ISoundTriggerModule module = mService.attach(0, callback);
+
+ // Load the model.
+ final int hwHandle = 7;
+ int handle = loadGenericModel(module, hwHandle).first;
+
+ // Start the model.
+ doThrow(new RecoverableException(Status.RESOURCE_CONTENTION)).when(
+ mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+
+ try {
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ module.startRecognition(handle, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ verify(mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+ }
+
+ @Test
public void testStartStopPhraseRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -953,7 +274,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -967,15 +287,15 @@ public class SoundTriggerMiddlewareImplTest {
startRecognition(module, handle, hwHandle);
// Signal a capture from the driver.
- hwCallback.sendRecognitionEvent(hwHandle,
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+ RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+ RecognitionStatus.SUCCESS);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+ assertEquals(event, eventCaptor.getValue());
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -984,7 +304,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testPhraseRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -998,15 +317,15 @@ public class SoundTriggerMiddlewareImplTest {
startRecognition(module, handle, hwHandle);
// Signal a capture from the driver.
- hwCallback.sendPhraseRecognitionEvent(hwHandle,
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+ PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+ RecognitionStatus.SUCCESS);
ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+ assertEquals(event, eventCaptor.getValue());
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1015,14 +334,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testForceRecognition() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -1037,18 +348,18 @@ public class SoundTriggerMiddlewareImplTest {
// Force a trigger.
module.forceRecognitionEvent(handle);
- verify(driver).getModelState(hwHandle);
+ verify(mHalDriver).forceRecognitionEvent(hwHandle);
// Signal a capture from the driver.
- // '3' means 'forced', there's no constant for that in the HAL.
- hwCallback.sendRecognitionEvent(hwHandle, 3);
+ RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+ RecognitionStatus.FORCED);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+ assertEquals(event, eventCaptor.getValue());
// Stop the recognition.
stopRecognition(module, handle, hwHandle);
@@ -1059,41 +370,27 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testForcePhraseRecognition() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
- initService(true);
+ public void testForceRecognitionNotSupported() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
// Load the model.
final int hwHandle = 17;
- Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadGenericModel(module, hwHandle);
int handle = modelHandles.first;
- SoundTriggerHwCallback hwCallback = modelHandles.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
// Force a trigger.
- module.forceRecognitionEvent(handle);
- verify(driver).getModelState(hwHandle);
-
- // Signal a capture from the driver.
- // '3' means 'forced', there's no constant for that in the HAL.
- hwCallback.sendPhraseRecognitionEvent(hwHandle, 3);
-
- ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
-
- // Validate the event.
- validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+ doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+ mHalDriver).forceRecognitionEvent(hwHandle);
+ try {
+ module.forceRecognitionEvent(handle);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
// Stop the recognition.
stopRecognition(module, handle, hwHandle);
@@ -1104,47 +401,36 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testAbortRecognition() throws Exception {
- // Make sure the HAL doesn't support concurrent capture.
- initService(false);
- mService.setCaptureState(false);
-
+ public void testForcePhraseRecognition() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
// Load the model.
- final int hwHandle = 11;
- int handle = loadGenericModel(module, hwHandle).first;
+ final int hwHandle = 17;
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ int handle = modelHandles.first;
+ SoundTriggerHwCallback hwCallback = modelHandles.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Abort.
- mService.setCaptureState(true);
-
- ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ // Force a trigger.
+ module.forceRecognitionEvent(handle);
+ verify(mHalDriver).forceRecognitionEvent(hwHandle);
- // Validate the event.
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+ // Signal a capture from the driver.
+ PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+ RecognitionStatus.FORCED);
- // Make sure we are notified of the lost availability.
- verify(callback).onRecognitionAvailabilityChange(false);
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
- // Attempt to start a new recognition - should get an abort event immediately, without
- // involving the HAL.
- clearInvocations(callback);
- clearInvocations(mHalDriver);
- module.startRecognition(handle, createRecognitionConfig());
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
- verifyNotStartRecognition();
+ // Validate the event.
+ assertEquals(event, eventCaptor.getValue());
- // Now enable it and make sure we are notified.
- mService.setCaptureState(false);
- verify(callback).onRecognitionAvailabilityChange(true);
+ // Stop the recognition.
+ stopRecognition(module, handle, hwHandle);
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1152,47 +438,30 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testAbortPhraseRecognition() throws Exception {
- // Make sure the HAL doesn't support concurrent capture.
- initService(false);
- mService.setCaptureState(false);
-
+ public void testForcePhraseRecognitionNotSupported() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
// Load the model.
- final int hwHandle = 11;
- int handle = loadPhraseModel(module, hwHandle).first;
+ final int hwHandle = 17;
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ int handle = modelHandles.first;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Abort.
- mService.setCaptureState(true);
-
- ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
-
- // Validate the event.
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
-
- // Make sure we are notified of the lost availability.
- verify(callback).onRecognitionAvailabilityChange(false);
-
- // Attempt to start a new recognition - should get an abort event immediately, without
- // involving the HAL.
- clearInvocations(callback);
- clearInvocations(mHalDriver);
- module.startRecognition(handle, createRecognitionConfig());
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
- verifyNotStartRecognition();
+ // Force a trigger.
+ doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+ mHalDriver).forceRecognitionEvent(hwHandle);
+ try {
+ module.forceRecognitionEvent(handle);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
- // Now enable it and make sure we are notified.
- mService.setCaptureState(false);
- verify(callback).onRecognitionAvailabilityChange(true);
+ // Stop the recognition.
+ stopRecognition(module, handle, hwHandle);
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1200,252 +469,156 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testNotAbortRecognitionConcurrent() throws Exception {
- // Make sure the HAL supports concurrent capture.
- initService(true);
-
+ public void testAbortRecognition() throws Exception {
+ // Make sure the HAL doesn't support concurrent capture.
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- clearInvocations(callback);
// Load the model.
- final int hwHandle = 13;
- int handle = loadGenericModel(module, hwHandle).first;
+ final int hwHandle = 11;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Signal concurrent capture. Shouldn't abort.
- mService.setCaptureState(true);
- verify(callback, never()).onRecognition(anyInt(), any());
- verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
+ // Abort.
+ hwCallback.sendRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
- // Stop the recognition.
- stopRecognition(module, handle, hwHandle);
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
- // Initiating a new one should work fine.
- clearInvocations(mHalDriver);
- startRecognition(module, handle, hwHandle);
- verify(callback, never()).onRecognition(anyInt(), any());
- stopRecognition(module, handle, hwHandle);
+ // Validate the event.
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
// Unload the model.
- module.unloadModel(handle);
+ unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
- public void testNotAbortPhraseRecognitionConcurrent() throws Exception {
- // Make sure the HAL supports concurrent capture.
- initService(true);
-
+ public void testAbortPhraseRecognition() throws Exception {
+ // Make sure the HAL doesn't support concurrent capture.
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- clearInvocations(callback);
// Load the model.
- final int hwHandle = 13;
- int handle = loadPhraseModel(module, hwHandle).first;
+ final int hwHandle = 11;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadPhraseModel(module, hwHandle);
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Signal concurrent capture. Shouldn't abort.
- mService.setCaptureState(true);
- verify(callback, never()).onPhraseRecognition(anyInt(), any());
- verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
+ // Abort.
+ hwCallback.sendPhraseRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
- // Stop the recognition.
- stopRecognition(module, handle, hwHandle);
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
- // Initiating a new one should work fine.
- clearInvocations(mHalDriver);
- startRecognition(module, handle, hwHandle);
- verify(callback, never()).onRecognition(anyInt(), any());
- stopRecognition(module, handle, hwHandle);
+ // Validate the event.
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
// Unload the model.
- module.unloadModel(handle);
+ unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
public void testParameterSupported() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 12;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer((Answer<Void>) invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
- resultCallback = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_3.ModelParameterRange range =
- new android.hardware.soundtrigger.V2_3.ModelParameterRange();
- range.start = 23;
- range.end = 45;
- OptionalModelParameterRange optionalRange = new OptionalModelParameterRange();
- optionalRange.range(range);
- resultCallback.onValues(0, optionalRange);
- return null;
- }).when(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
-
- ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
- ModelParameter.THRESHOLD_FACTOR);
+ ModelParameterRange halRange = new ModelParameterRange();
+ halRange.minInclusive = 23;
+ halRange.maxInclusive = 45;
- verify(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
-
- assertEquals(23, range.minInclusive);
- assertEquals(45, range.maxInclusive);
- }
-
- @Test
- public void testParameterNotSupportedOld() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- return;
- }
-
- initService(false);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- final int hwHandle = 13;
- int modelHandle = loadGenericModel(module, hwHandle).first;
+ when(mHalDriver.queryParameter(eq(hwHandle),
+ eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(halRange);
ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
ModelParameter.THRESHOLD_FACTOR);
- assertNull(range);
+ verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
+
+ assertEquals(halRange, range);
}
@Test
public void testParameterNotSupported() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 13;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
- resultCallback = invocation.getArgument(2);
- // This is the return of this method.
- resultCallback.onValues(0, new OptionalModelParameterRange());
- return null;
- }).when(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ when(mHalDriver.queryParameter(eq(hwHandle),
+ eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(null);
ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
ModelParameter.THRESHOLD_FACTOR);
- verify(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
assertNull(range);
}
@Test
public void testGetParameter() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 14;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
- resultCallback = invocation.getArgument(2);
- // This is the return of this method.
- resultCallback.onValues(0, 234);
- return null;
- }).when(driver).getParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ when(mHalDriver.getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR)).thenReturn(
+ 234);
int value = module.getModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR);
- verify(driver).getParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR);
assertEquals(234, value);
}
@Test
public void testSetParameter() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 17;
int modelHandle = loadGenericModel(module, hwHandle).first;
- when(driver.setParameter(hwHandle,
- android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
- 456)).thenReturn(0);
-
module.setModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR, 456);
- verify(driver).setParameter(hwHandle,
- android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR, 456);
+ verify(mHalDriver).setModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR, 456);
}
private static class SoundTriggerHwCallback {
- private final android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback mCallback;
- private final int mCookie;
+ private final ISoundTriggerHal.ModelCallback mCallback;
- SoundTriggerHwCallback(android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback,
- int cookie) {
+ SoundTriggerHwCallback(ISoundTriggerHal.ModelCallback callback) {
mCallback = callback;
- mCookie = cookie;
}
- private void sendRecognitionEvent(int hwHandle, int status) throws RemoteException {
- if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
- ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).recognitionCallback_2_1(
- createRecognitionEvent_2_1(hwHandle, status), mCookie);
- } else {
- mCallback.recognitionCallback(createRecognitionEvent_2_0(hwHandle, status),
- mCookie);
- }
+ private RecognitionEvent sendRecognitionEvent(int hwHandle, @RecognitionStatus int status) {
+ RecognitionEvent event = TestUtil.createRecognitionEvent(status);
+ mCallback.recognitionCallback(hwHandle, event);
+ return event;
+ }
+
+ private PhraseRecognitionEvent sendPhraseRecognitionEvent(int hwHandle,
+ @RecognitionStatus int status) {
+ PhraseRecognitionEvent event = TestUtil.createPhraseRecognitionEvent(status);
+ mCallback.phraseRecognitionCallback(hwHandle, event);
+ return event;
}
- private void sendPhraseRecognitionEvent(int hwHandle, int status) throws RemoteException {
- if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
- ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).phraseRecognitionCallback_2_1(
- createPhraseRecognitionEvent_2_1(hwHandle, status), mCookie);
- } else {
- mCallback.phraseRecognitionCallback(
- createPhraseRecognitionEvent_2_0(hwHandle, status), mCookie);
- }
+ private void sendUnloadEvent(int hwHandle) {
+ mCallback.modelUnloaded(hwHandle);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
new file mode 100644
index 000000000000..4eca298f68a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -0,0 +1,446 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback;
+import android.media.audio.common.AudioChannelMask;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioFormat;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.os.HidlMemoryUtil;
+import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Test utilities, aimed at generating populated objects of the various types and validating
+ * corresponding objects generated by the system under test.
+ */
+class TestUtil {
+ static SoundModel createGenericSoundModel() {
+ return createSoundModel(SoundModelType.GENERIC);
+ }
+
+ private static SoundModel createSoundModel(@SoundModelType int type) {
+ SoundModel model = new SoundModel();
+ model.type = type;
+ model.uuid = "12345678-2345-3456-4567-abcdef987654";
+ model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+ byte[] data = new byte[]{91, 92, 93, 94, 95};
+ model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
+ model.dataSize = data.length;
+ return model;
+ }
+
+ private static void validateSoundModel_2_1(ISoundTriggerHw.SoundModel model, int type) {
+ assertEquals(type, model.header.type);
+ assertEquals("12345678-2345-3456-4567-abcdef987654",
+ ConversionUtil.hidl2aidlUuid(model.header.uuid));
+ assertEquals("87654321-5432-6543-7654-456789fedcba",
+ ConversionUtil.hidl2aidlUuid(model.header.vendorUuid));
+ assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
+ HidlMemoryUtil.hidlMemoryToByteArray(model.data));
+ }
+
+ private static void validateSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
+ assertEquals(type, model.type);
+ assertEquals("12345678-2345-3456-4567-abcdef987654",
+ ConversionUtil.hidl2aidlUuid(model.uuid));
+ assertEquals("87654321-5432-6543-7654-456789fedcba",
+ ConversionUtil.hidl2aidlUuid(model.vendorUuid));
+ assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, model.data.toArray());
+ }
+
+ static void validateGenericSoundModel_2_1(ISoundTriggerHw.SoundModel model) {
+ validateSoundModel_2_1(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+ }
+
+ static void validateGenericSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model) {
+ validateSoundModel_2_0(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+ }
+
+ static PhraseSoundModel createPhraseSoundModel() {
+ PhraseSoundModel model = new PhraseSoundModel();
+ model.common = createSoundModel(SoundModelType.KEYPHRASE);
+ model.phrases = new Phrase[1];
+ model.phrases[0] = new Phrase();
+ model.phrases[0].id = 123;
+ model.phrases[0].users = new int[]{5, 6, 7};
+ model.phrases[0].locale = "locale";
+ model.phrases[0].text = "text";
+ model.phrases[0].recognitionModes =
+ RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+ return model;
+ }
+
+ static void validatePhraseSoundModel_2_1(ISoundTriggerHw.PhraseSoundModel model) {
+ validateSoundModel_2_1(model.common,
+ android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+ validatePhrases_2_0(model.phrases);
+ }
+
+ static void validatePhraseSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
+ validateSoundModel_2_0(model.common,
+ android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+ validatePhrases_2_0(model.phrases);
+ }
+
+ private static void validatePhrases_2_0(
+ List<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase> phrases) {
+ assertEquals(1, phrases.size());
+ assertEquals(123, phrases.get(0).id);
+ assertArrayEquals(new Integer[]{5, 6, 7}, phrases.get(0).users.toArray());
+ assertEquals("locale", phrases.get(0).locale);
+ assertEquals("text", phrases.get(0).text);
+ assertEquals(RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION,
+ phrases.get(0).recognitionModes);
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties_2_0(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
+ properties.implementor = "implementor";
+ properties.description = "description";
+ properties.version = 123;
+ properties.uuid.timeLow = 1;
+ properties.uuid.timeMid = 2;
+ properties.uuid.versionAndTimeHigh = 3;
+ properties.uuid.variantAndClockSeqHigh = 4;
+ properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
+
+ properties.maxSoundModels = 456;
+ properties.maxKeyPhrases = 567;
+ properties.maxUsers = 678;
+ properties.recognitionModes =
+ android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ properties.captureTransition = true;
+ properties.maxBufferMs = 321;
+ properties.concurrentCapture = supportConcurrentCapture;
+ properties.triggerInEvent = true;
+ properties.powerConsumptionMw = 432;
+ return properties;
+ }
+
+ static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties.base = createDefaultProperties_2_0(supportConcurrentCapture);
+ properties.supportedModelArch = "supportedModelArch";
+ properties.audioCapabilities =
+ android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
+ | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
+ return properties;
+ }
+
+ static Properties createDefaultProperties(boolean supportConcurrentCapture) {
+ Properties properties = new Properties();
+ properties.implementor = "implementor";
+ properties.description = "description";
+ properties.version = 123;
+ properties.uuid = "00000001-0002-0003-0004-05060708090a";
+ properties.maxSoundModels = 456;
+ properties.maxKeyPhrases = 567;
+ properties.maxUsers = 678;
+ properties.recognitionModes =
+ RecognitionMode.VOICE_TRIGGER
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.GENERIC_TRIGGER;
+ properties.captureTransition = true;
+ properties.maxBufferMs = 321;
+ properties.concurrentCapture = supportConcurrentCapture;
+ properties.triggerInEvent = true;
+ properties.powerConsumptionMw = 432;
+ properties.supportedModelArch = "supportedModelArch";
+ properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION;
+ return properties;
+ }
+
+ static void validateDefaultProperties(Properties properties,
+ boolean supportConcurrentCapture) {
+ validateDefaultProperties(properties, supportConcurrentCapture,
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+ "supportedModelArch");
+ }
+
+ static void validateDefaultProperties(Properties properties,
+ boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
+ @NonNull String supportedModelArch) {
+ assertEquals("implementor", properties.implementor);
+ assertEquals("description", properties.description);
+ assertEquals(123, properties.version);
+ assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
+ assertEquals(456, properties.maxSoundModels);
+ assertEquals(567, properties.maxKeyPhrases);
+ assertEquals(678, properties.maxUsers);
+ assertEquals(RecognitionMode.GENERIC_TRIGGER
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+ assertTrue(properties.captureTransition);
+ assertEquals(321, properties.maxBufferMs);
+ assertEquals(supportConcurrentCapture, properties.concurrentCapture);
+ assertTrue(properties.triggerInEvent);
+ assertEquals(432, properties.powerConsumptionMw);
+ assertEquals(supportedModelArch, properties.supportedModelArch);
+ assertEquals(audioCapabilities, properties.audioCapabilities);
+ }
+
+ static RecognitionConfig createRecognitionConfig() {
+ RecognitionConfig config = new RecognitionConfig();
+ config.captureRequested = true;
+ config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
+ config.phraseRecognitionExtras[0].id = 123;
+ config.phraseRecognitionExtras[0].confidenceLevel = 4;
+ config.phraseRecognitionExtras[0].recognitionModes = 5;
+ config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
+ config.phraseRecognitionExtras[0].levels[0].userId = 234;
+ config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
+ config.data = new byte[]{5, 4, 3, 2, 1};
+ config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION;
+ return config;
+ }
+
+ static void validateRecognitionConfig_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config,
+ int captureDevice, int captureHandle) {
+ assertTrue(config.captureRequested);
+ assertEquals(captureDevice, config.captureDevice);
+ assertEquals(captureHandle, config.captureHandle);
+ assertEquals(1, config.phrases.size());
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+ config.phrases.get(0);
+ assertEquals(123, halPhraseExtra.id);
+ assertEquals(4, halPhraseExtra.confidenceLevel);
+ assertEquals(5, halPhraseExtra.recognitionModes);
+ assertEquals(1, halPhraseExtra.levels.size());
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+ assertEquals(234, halLevel.userId);
+ assertEquals(34, halLevel.levelPercent);
+ assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, config.data.toArray());
+ }
+
+ static void validateRecognitionConfig_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+ int captureDevice, int captureHandle) {
+ assertTrue(config.header.captureRequested);
+ assertEquals(captureDevice, config.header.captureDevice);
+ assertEquals(captureHandle, config.header.captureHandle);
+ assertEquals(1, config.header.phrases.size());
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+ config.header.phrases.get(0);
+ assertEquals(123, halPhraseExtra.id);
+ assertEquals(4, halPhraseExtra.confidenceLevel);
+ assertEquals(5, halPhraseExtra.recognitionModes);
+ assertEquals(1, halPhraseExtra.levels.size());
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+ assertEquals(234, halLevel.userId);
+ assertEquals(34, halLevel.levelPercent);
+ assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
+ HidlMemoryUtil.hidlMemoryToByteArray(config.data));
+ }
+
+ static void validateRecognitionConfig_2_3(
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config, int captureDevice,
+ int captureHandle) {
+ validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
+
+ assertEquals(AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
+ int hwHandle,
+ int status) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
+ halEvent.status = status;
+ halEvent.type = SoundModelType.GENERIC;
+ halEvent.model = hwHandle;
+ halEvent.captureAvailable = true;
+ // This field is ignored.
+ halEvent.captureSession = 9999;
+ halEvent.captureDelayMs = 234;
+ halEvent.capturePreambleMs = 345;
+ halEvent.triggerInData = true;
+ halEvent.audioConfig.sampleRateHz = 456;
+ halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+ halEvent.audioConfig.format = AudioFormat.MP3;
+ // hwEvent.audioConfig.offloadInfo is irrelevant.
+ halEvent.data.add((byte) 31);
+ halEvent.data.add((byte) 32);
+ halEvent.data.add((byte) 33);
+ return halEvent;
+ }
+
+ static RecognitionEvent createRecognitionEvent(@RecognitionStatus int status) {
+ RecognitionEvent event = new RecognitionEvent();
+ event.status = status;
+ event.type = SoundModelType.GENERIC;
+ event.captureAvailable = true;
+ event.captureDelayMs = 234;
+ event.capturePreambleMs = 345;
+ event.triggerInData = true;
+ event.audioConfig = new AudioConfig();
+ event.audioConfig.sampleRateHz = 456;
+ event.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+ event.audioConfig.format = AudioFormat.MP3;
+ //event.audioConfig.offloadInfo is irrelevant.
+ event.data = new byte[]{31, 32, 33};
+ return event;
+ }
+
+ static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
+ int hwHandle,
+ int status) {
+ ISoundTriggerHwCallback.RecognitionEvent halEvent =
+ new ISoundTriggerHwCallback.RecognitionEvent();
+ halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
+ halEvent.header.data.clear();
+ halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
+ return halEvent;
+ }
+
+ static void validateRecognitionEvent(RecognitionEvent event, @RecognitionStatus int status) {
+ assertEquals(status, event.status);
+ assertEquals(SoundModelType.GENERIC, event.type);
+ assertTrue(event.captureAvailable);
+ assertEquals(234, event.captureDelayMs);
+ assertEquals(345, event.capturePreambleMs);
+ assertTrue(event.triggerInData);
+ assertEquals(456, event.audioConfig.sampleRateHz);
+ assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
+ assertEquals(AudioFormat.MP3, event.audioConfig.format);
+ assertArrayEquals(new byte[]{31, 32, 33}, event.data);
+ }
+
+ static PhraseRecognitionEvent createPhraseRecognitionEvent(@RecognitionStatus int status) {
+ PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+ event.common = createRecognitionEvent(status);
+
+ PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
+ extra.id = 123;
+ extra.confidenceLevel = 52;
+ extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
+ | RecognitionMode.GENERIC_TRIGGER;
+ ConfidenceLevel level = new ConfidenceLevel();
+ level.userId = 31;
+ level.levelPercent = 43;
+ extra.levels = new ConfidenceLevel[]{level};
+ event.phraseExtras = new PhraseRecognitionExtra[]{extra};
+ return event;
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
+ createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+ halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
+
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+ new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+ halExtra.id = 123;
+ halExtra.confidenceLevel = 52;
+ halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+ new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+ halLevel.userId = 31;
+ halLevel.levelPercent = 43;
+ halExtra.levels.add(halLevel);
+ halEvent.phraseExtras.add(halExtra);
+ return halEvent;
+ }
+
+ static ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
+ int hwHandle, int status) {
+ ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+ new ISoundTriggerHwCallback.PhraseRecognitionEvent();
+ halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
+
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+ new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+ halExtra.id = 123;
+ halExtra.confidenceLevel = 52;
+ halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+ new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+ halLevel.userId = 31;
+ halLevel.levelPercent = 43;
+ halExtra.levels.add(halLevel);
+ halEvent.phraseExtras.add(halExtra);
+ return halEvent;
+ }
+
+ static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event,
+ @RecognitionStatus int status) {
+ validateRecognitionEvent(event.common, status);
+
+ assertEquals(1, event.phraseExtras.length);
+ assertEquals(123, event.phraseExtras[0].id);
+ assertEquals(52, event.phraseExtras[0].confidenceLevel);
+ assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
+ event.phraseExtras[0].recognitionModes);
+ assertEquals(1, event.phraseExtras[0].levels.length);
+ assertEquals(31, event.phraseExtras[0].levels[0].userId);
+ assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
+ }
+
+ private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
+ try {
+ SharedMemory shmem = SharedMemory.create("", data.length);
+ ByteBuffer buffer = shmem.mapReadWrite();
+ buffer.put(data);
+ return shmem.getFileDescriptor();
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
new file mode 100644
index 000000000000..65a97596cd73
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Test that the ledger records transactions correctly. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LedgerTest {
+
+ @Test
+ public void testInitialState() {
+ final Ledger ledger = new Ledger();
+ assertEquals(0, ledger.getCurrentBalance());
+ assertEquals(0, ledger.get24HourSum(0, 0));
+ }
+
+ @Test
+ public void testMultipleTransactions() {
+ final Ledger ledger = new Ledger();
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5));
+ assertEquals(5, ledger.getCurrentBalance());
+ assertEquals(5, ledger.get24HourSum(1, 60_000));
+ ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25));
+ assertEquals(30, ledger.getCurrentBalance());
+ assertEquals(30, ledger.get24HourSum(1, 60_000));
+ ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10));
+ assertEquals(20, ledger.getCurrentBalance());
+ assertEquals(20, ledger.get24HourSum(1, 60_000));
+ }
+
+ @Test
+ public void test24HourSum() {
+ final Ledger ledger = new Ledger();
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500));
+ assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+ ledger.recordTransaction(
+ new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500));
+ assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+ ledger.recordTransaction(
+ new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1));
+ assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+ assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
+ assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
+ // Pro-rated as the second transaction phases out
+ assertEquals(1251,
+ ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS));
+ assertEquals(1, ledger.get24HourSum(1, 27 * HOUR_IN_MILLIS));
+ assertEquals(0, ledger.get24HourSum(1, 28 * HOUR_IN_MILLIS));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/OWNERS b/services/tests/servicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 000000000000..217a5edff08b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 8af2c4d04fd9..28838ae14f2f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -97,7 +97,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.getCapabilitiesAndConfig();
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -129,7 +129,7 @@ public class TimeZoneDetectorServiceTest {
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
try {
mTimeZoneDetectorService.addListener(mockListener);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -235,7 +235,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME_ZONE),
@@ -268,7 +268,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
@@ -301,7 +301,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
@@ -317,7 +317,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index a0e9d977954f..036362540bf1 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.timezonedetector.location;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -728,6 +729,9 @@ public class ControllerImplTest {
// Simulate the user change (but geo detection still enabled).
testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
+ // Confirm that the previous suggestion was overridden.
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+
// We expect the provider to end up in PROVIDER_STATE_STARTED_INITIALIZING, but it should
// have been stopped when the user changed.
int[] expectedStateTransitions =
@@ -1068,6 +1072,48 @@ public class ControllerImplTest {
}
}
+ @Test
+ public void destroy() {
+ ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+ mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+ // Initialize and check initial state.
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+ PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestCallback.assertNoSuggestionMade();
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+ // Simulate the primary provider suggesting a time zone.
+ mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ // Receiving a "success" provider event should cause a suggestion to be made synchronously,
+ // and also clear the scheduled uncertainty suggestion.
+ mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+ PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+ // Trigger destroy().
+ controllerImpl.destroy();
+
+ // Confirm that the previous suggestion was overridden.
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+
+ mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(
+ PROVIDER_STATE_STOPPED, PROVIDER_STATE_DESTROYED);
+ mTestSecondaryLocationTimeZoneProvider.assertStateChangesAndCommit(
+ PROVIDER_STATE_DESTROYED);
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ }
+
private static void assertUncertaintyTimeoutSet(
LocationTimeZoneProviderController.Environment environment,
LocationTimeZoneProviderController controller) {
@@ -1175,7 +1221,6 @@ public class ControllerImplTest {
private final TestState<ProviderState> mTestProviderState = new TestState<>();
private boolean mFailDuringInitialization;
private boolean mInitialized;
- private boolean mDestroyed;
/**
* Creates the instance.
@@ -1200,7 +1245,7 @@ public class ControllerImplTest {
@Override
void onDestroy() {
- mDestroyed = true;
+ // No behavior needed.
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index aadab6ea4fd9..2f36c7fb9044 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -365,13 +365,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -415,13 +415,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -511,13 +511,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -567,13 +567,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init cicam/cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -697,13 +697,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init lnb resources.
int[] lnbHandles = {1};
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index b11c85c008ad..c611e38b068a 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -74,3 +74,17 @@ android_test_helper_app {
resource_dirs: ["res"],
manifest: "AndroidManifestApp4.xml",
}
+
+android_test_helper_app {
+ name: "PackageParserTestApp5",
+ sdk_version: "current",
+ srcs: ["**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ resource_dirs: ["res"],
+ manifest: "AndroidManifestApp5.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
new file mode 100644
index 000000000000..cc6caad8e2e3
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.packageparserapp" >
+
+ <uses-sdk android:minSdkVersion="3"
+ android:targetSdkVersion="3" />
+
+ <application>
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index fd6804641d2c..0169a7d96f9f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -16,10 +16,13 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -116,4 +119,15 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase {
SmallHash.hash(group.hashCode()),
p.getGroupIdHash());
}
+
+ @Test
+ public void testIsForegroundService() {
+ NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+ 0, null);
+ assertFalse(NotificationRecordLogger.isForegroundService(p.r));
+
+ // Set foreground service
+ p.r.getSbn().getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ assertTrue(NotificationRecordLogger.isForegroundService(p.r));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 11162043bb27..9ad007d6a840 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -36,6 +36,7 @@ import org.junit.runner.RunWith;
import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.TimeZone;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -171,6 +172,119 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
}
@Test
+ public void testGetNextChangeTime_startTomorrowInDaylight() {
+ // Test that the correct thing happens when the next start time would be tomorrow, during
+ // a schedule start time that doesn't exist that day. Consistent with "start times" as
+ // implemented in isInSchedule, this should get adjusted to the closest actual time.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // "today" = the day before the skipped hour for daylight savings.
+ Calendar today = getDaylightSavingsForwardDay();
+ today.set(Calendar.HOUR_OF_DAY, 23);
+ today.set(Calendar.MINUTE, 15);
+ Calendar tomorrow = getDaylightSavingsForwardDay();
+ tomorrow.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+ tomorrow.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.endHour = 4;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endMinute = 15;
+ mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // The expected next change time should be tomorrow, 3AM as 2:15AM doesn't exist.
+ Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ expected.setTimeInMillis(tomorrow.getTimeInMillis());
+ expected.set(Calendar.HOUR_OF_DAY, 3);
+ expected.set(Calendar.MINUTE, 0);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+ GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ actual.setTimeInMillis(actualMs);
+ assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+ actualMs);
+ }
+
+ @Test
+ public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylight() {
+ // Test that the correct thing happens when the next start time would be tomorrow, but
+ // today is the day when daylight time switches over (so the "schedule start time" today
+ // may not exist).
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // "today" = the day with the skipped hour for daylight savings.
+ Calendar today = getDaylightSavingsForwardDay();
+ today.add(Calendar.DATE, 1);
+ today.set(Calendar.HOUR_OF_DAY, 23);
+ today.set(Calendar.MINUTE, 15);
+ Calendar tomorrow = getDaylightSavingsForwardDay();
+ tomorrow.add(Calendar.DATE, 2);
+ mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+ tomorrow.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.endHour = 4;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endMinute = 15;
+ mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // The expected next change time should be tomorrow, 2:15AM.
+ Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ expected.setTimeInMillis(tomorrow.getTimeInMillis());
+ expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+ expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+ GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ actual.setTimeInMillis(actualMs);
+ assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+ actualMs);
+ }
+
+ @Test
+ public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylightBackward() {
+ // Test that the correct thing happens when the next start time would be tomorrow, but
+ // today is the day when clocks are adjusted backwards (so the "schedule start time" today
+ // exists twice).
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // "today" = the day with the extra hour for daylight savings.
+ Calendar today = getDaylightSavingsBackwardDay();
+ today.add(Calendar.DATE, 1);
+ today.set(Calendar.HOUR_OF_DAY, 23);
+ today.set(Calendar.MINUTE, 15);
+ Calendar tomorrow = getDaylightSavingsBackwardDay();
+ tomorrow.add(Calendar.DATE, 2);
+ mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+ tomorrow.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 1;
+ mScheduleInfo.endHour = 4;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endMinute = 15;
+ mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // The expected next change time should be tomorrow, 1:15AM.
+ Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ expected.setTimeInMillis(tomorrow.getTimeInMillis());
+ expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+ expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+ GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ actual.setTimeInMillis(actualMs);
+ assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+ actualMs);
+ }
+
+ @Test
public void testShouldExitForAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 1000;
@@ -416,22 +530,264 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
}
@Test
+ public void testIsInSchedule_daylightSavingsForward_startDuringChange() {
+ // Test that if the start time of a ScheduleCalendar is during the nonexistent
+ // hour of daylight savings forward time, the evaluation of whether a time is in the
+ // schedule still works.
+
+ // Set timezone to make sure we're evaluating the correct days.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 2:30AM - 4:00AM.
+ final Calendar dstYesterday = getDaylightSavingsForwardDay();
+ final Calendar dstToday = getDaylightSavingsForwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.startMinute = 30;
+ mScheduleInfo.endHour = 4;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // Test cases: there are 2 "on" periods. These cover: before the first schedule
+ // (1AM previous day), during the first schedule (2:30AM), two between the two schedules
+ // (one on each calendar day), during the second (3:30AM), and after the second (4:30AM)
+ Calendar out1 = getDaylightSavingsForwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 1);
+ out1.set(Calendar.MINUTE, 00);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in1 = getDaylightSavingsForwardDay();
+ in1.set(Calendar.HOUR_OF_DAY, 2);
+ in1.set(Calendar.MINUTE, 45);
+ in1.set(Calendar.SECOND, 0);
+ in1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut1 = getDaylightSavingsForwardDay();
+ midOut1.set(Calendar.HOUR_OF_DAY, 7);
+ midOut1.set(Calendar.MINUTE, 30);
+ midOut1.set(Calendar.SECOND, 0);
+ midOut1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut2 = getDaylightSavingsForwardDay();
+ midOut2.add(Calendar.DATE, 1);
+ midOut2.set(Calendar.HOUR_OF_DAY, 1);
+ midOut2.set(Calendar.MINUTE, 30);
+ midOut2.set(Calendar.SECOND, 0);
+ midOut2.set(Calendar.MILLISECOND, 0);
+
+ // Question: should 3:15AM be in the 2:30-4 schedule on a day when 2:30-3 doesn't exist?
+ Calendar in2 = getDaylightSavingsForwardDay();
+ in2.add(Calendar.DATE, 1);
+ in2.set(Calendar.HOUR_OF_DAY, 3);
+ in2.set(Calendar.MINUTE, 30);
+ in2.set(Calendar.SECOND, 0);
+ in2.set(Calendar.MILLISECOND, 0);
+
+ Calendar out2 = getDaylightSavingsForwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 4);
+ out2.set(Calendar.MINUTE, 30);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsInSchedule_daylightSavingsForward_endDuringChange() {
+ // Test that if the end time of a ScheduleCalendar is during the nonexistent
+ // hour of daylight savings forward time, the evaluation of whether a time is in the
+ // schedule still works.
+
+ // Set timezone to make sure we're evaluating the correct days.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 11:00PM - 2:30AM. On the day when 2AM doesn't exist, this should
+ // effectively finish at 3:30AM(?)
+ final Calendar dstYesterday = getDaylightSavingsForwardDay();
+ final Calendar dstToday = getDaylightSavingsForwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 23;
+ mScheduleInfo.endHour = 2;
+ mScheduleInfo.endMinute = 30;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // Test cases: before the time period on the previous day; during the time period when
+ // the calendar day is still the previous day; during the time period when the calendar
+ // day is the change day; afterwards.
+ Calendar out1 = getDaylightSavingsForwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 22);
+ out1.set(Calendar.MINUTE, 00);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in1 = getDaylightSavingsForwardDay();
+ in1.set(Calendar.HOUR_OF_DAY, 23);
+ in1.set(Calendar.MINUTE, 30);
+ in1.set(Calendar.SECOND, 0);
+ in1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in2 = getDaylightSavingsForwardDay();
+ in2.add(Calendar.DATE, 1);
+ in2.set(Calendar.HOUR_OF_DAY, 1);
+ in2.set(Calendar.MINUTE, 30);
+ in2.set(Calendar.SECOND, 0);
+ in2.set(Calendar.MILLISECOND, 0);
+
+ // Question: Should 3:15AM be out of the schedule on a day when 2-3 doesn't exist?
+ Calendar out2 = getDaylightSavingsForwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 3);
+ out2.set(Calendar.MINUTE, 45);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsInSchedule_daylightSavingsBackward_startDuringChange() {
+ // Test that if the start time of a ScheduleCalendar is during the duplicated
+ // hour of daylight savings backward time, the evaluation of whether a time is in the
+ // schedule still works. It's not clear what correct behavior is during the duplicated
+ // 1:00->1:59->1:00->1:59 time period, but times outside that should still work.
+
+ // Set timezone to make sure we're evaluating the correct days.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 1:15AM - 4:00AM.
+ final Calendar dstYesterday = getDaylightSavingsBackwardDay();
+ final Calendar dstToday = getDaylightSavingsBackwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 1;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endHour = 4;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // Test cases: there are 2 "on" periods. These cover: before the first schedule
+ // (1AM previous day), during the first schedule (2:30AM), two between the two schedules
+ // (one on each calendar day), during the second (2:30AM), and after the second (4:30AM)
+ Calendar out1 = getDaylightSavingsBackwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 1);
+ out1.set(Calendar.MINUTE, 00);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in1 = getDaylightSavingsBackwardDay();
+ in1.set(Calendar.HOUR_OF_DAY, 2);
+ in1.set(Calendar.MINUTE, 30);
+ in1.set(Calendar.SECOND, 0);
+ in1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut1 = getDaylightSavingsBackwardDay();
+ midOut1.set(Calendar.HOUR_OF_DAY, 7);
+ midOut1.set(Calendar.MINUTE, 30);
+ midOut1.set(Calendar.SECOND, 0);
+ midOut1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut2 = getDaylightSavingsBackwardDay();
+ midOut2.add(Calendar.DATE, 1);
+ midOut2.set(Calendar.HOUR_OF_DAY, 0);
+ midOut2.set(Calendar.MINUTE, 30);
+ midOut2.set(Calendar.SECOND, 0);
+ midOut2.set(Calendar.MILLISECOND, 0);
+
+ Calendar in2 = getDaylightSavingsBackwardDay();
+ in2.add(Calendar.DATE, 1);
+ in2.set(Calendar.HOUR_OF_DAY, 2);
+ in2.set(Calendar.MINUTE, 30);
+ in2.set(Calendar.SECOND, 0);
+ in2.set(Calendar.MILLISECOND, 0);
+
+ Calendar out2 = getDaylightSavingsBackwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 4);
+ out2.set(Calendar.MINUTE, 30);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsInSchedule_daylightSavings_flippedSchedule() {
+ // This test is for the unlikely edge case where the skipped hour due to daylight savings
+ // causes the evaluated start time to be "later" than the schedule's end time on that day,
+ // for instance if the schedule is 2:30AM-3:15AM; 2:30AM may evaluate to 3:30AM on the day
+ // of daylight change.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 2:30AM - 3:15AM.
+ final Calendar dstYesterday = getDaylightSavingsForwardDay();
+ final Calendar dstToday = getDaylightSavingsForwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.startMinute = 30;
+ mScheduleInfo.endHour = 3;
+ mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // It may not be well-defined what times around the 2-3AM range one might expect to be
+ // included or not included on the weird day when 2AM doesn't exist, but other unrelated
+ // times of day (here, 3PM) should definitely be out.
+ Calendar out1 = getDaylightSavingsForwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 15);
+ out1.set(Calendar.MINUTE, 0);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar out2 = getDaylightSavingsForwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 15);
+ out2.set(Calendar.MINUTE, 0);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
public void testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger_daylightSavings() {
- Calendar alarm = getDaylightSavingsDay();
+ // Need to set the time zone explicitly to a US one so that the daylight savings time day is
+ // correct.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+ Calendar alarm = getDaylightSavingsForwardDay();
alarm.set(Calendar.HOUR_OF_DAY, 23);
alarm.set(Calendar.MINUTE, 15);
alarm.set(Calendar.SECOND, 0);
alarm.set(Calendar.MILLISECOND, 0);
- Calendar now = getDaylightSavingsDay();
+ Calendar now = getDaylightSavingsForwardDay();
now.set(Calendar.HOUR_OF_DAY, 2);
now.set(Calendar.MINUTE, 10);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
now.add(Calendar.DATE, 1); // add a day, on daylight savings this becomes 3:10am
- final Calendar tempToday = getDaylightSavingsDay();
- final Calendar tempTomorrow = getDaylightSavingsDay();
+ final Calendar tempToday = getDaylightSavingsForwardDay();
+ final Calendar tempTomorrow = getDaylightSavingsForwardDay();
tempTomorrow.add(Calendar.DATE, 1);
mScheduleInfo.days = new int[] {tempToday.get(Calendar.DAY_OF_WEEK),
tempTomorrow.get(Calendar.DAY_OF_WEEK)};
@@ -506,6 +862,80 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
now.getTimeInMillis()));
}
+ @Test
+ public void testClosestActualTime_regularTimesAndSkippedTime() {
+ // Make sure we're operating in the relevant time zone for the assumed Daylight Savings day
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+ Calendar day = getDaylightSavingsForwardDay();
+ day.set(Calendar.HOUR_OF_DAY, 15);
+ day.set(Calendar.MINUTE, 25);
+ day.set(Calendar.SECOND, 0);
+ day.set(Calendar.MILLISECOND, 0);
+ assertEquals(day.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25));
+
+ // Check a skipped time
+ day.add(Calendar.DATE, 1);
+ day.set(Calendar.HOUR_OF_DAY, 3);
+ day.set(Calendar.MINUTE, 0);
+ day.set(Calendar.SECOND, 0);
+ day.set(Calendar.MILLISECOND, 0);
+ assertEquals(day.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 2, 15));
+
+ // Check a non-skipped time after the clocks have moved forward
+ day.set(Calendar.HOUR_OF_DAY, 15);
+ day.set(Calendar.MINUTE, 25);
+ day.set(Calendar.SECOND, 0);
+ day.set(Calendar.MILLISECOND, 0);
+ assertEquals(day.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25));
+ }
+
+ @Test
+ public void testClosestActualTime_otherTimeZones() {
+ // Make sure this doesn't only work for US/Eastern time.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/London"));
+ Calendar ukDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/London"));
+ ukDstDay.set(2021, Calendar.MARCH, 28);
+
+ // Check a skipped time, which is 01:xx on that day in the UK
+ ukDstDay.set(Calendar.HOUR_OF_DAY, 2);
+ ukDstDay.set(Calendar.MINUTE, 0);
+ ukDstDay.set(Calendar.SECOND, 0);
+ ukDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(ukDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 1, 25));
+
+ // Check a non-skipped time
+ ukDstDay.set(Calendar.HOUR_OF_DAY, 11);
+ ukDstDay.set(Calendar.MINUTE, 23);
+ ukDstDay.set(Calendar.SECOND, 0);
+ ukDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(ukDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 11, 23));
+
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
+ Calendar frDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/Paris"));
+ frDstDay.set(2021, Calendar.MARCH, 28);
+
+ // Check a skipped time, which is 02:xx on that day in France
+ frDstDay.set(Calendar.HOUR_OF_DAY, 3);
+ frDstDay.set(Calendar.MINUTE, 0);
+ frDstDay.set(Calendar.SECOND, 0);
+ frDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(frDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 2, 25));
+
+ // Check a regular time
+ frDstDay.set(Calendar.HOUR_OF_DAY, 14);
+ frDstDay.set(Calendar.MINUTE, 59);
+ frDstDay.set(Calendar.SECOND, 0);
+ frDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(frDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 14, 59));
+ }
+
private int getTodayDay() {
return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
}
@@ -517,9 +947,23 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
}
- private Calendar getDaylightSavingsDay() {
- // the day before daylight savings in the US - March 9, 2019
- Calendar daylightSavingsDay = new GregorianCalendar(2019, 2, 9);
+ private Calendar getDaylightSavingsForwardDay() {
+ // the day before daylight savings rolls forward in the US - March 9, 2019
+ // 2AM March 10, 2019 does not exist -- goes straight from 1:59 to 3:00
+ // Specifically set to US/Eastern time zone rather than relying on a default time zone
+ // to make sure the date is the correct one, since DST changes vary by region.
+ Calendar daylightSavingsDay = new GregorianCalendar(
+ TimeZone.getTimeZone("America/New_York"));
+ daylightSavingsDay.set(2019, Calendar.MARCH, 9);
+ return daylightSavingsDay;
+ }
+
+ private Calendar getDaylightSavingsBackwardDay() {
+ // the day before daylight savings rolls backward in the US - November 2, 2019
+ // In this instance, 1AM November 3 2019 is repeated twice; 1:00->1:59->1:00->1:59->2:00
+ Calendar daylightSavingsDay = new GregorianCalendar(
+ TimeZone.getTimeZone("America/New_York"));
+ daylightSavingsDay.set(2019, Calendar.NOVEMBER, 2);
return daylightSavingsDay;
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 4410404b0cd7..31be33e98363 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -28,6 +28,7 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CA
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
@@ -1263,6 +1264,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
| SUPPRESSED_EFFECT_LIGHTS
+ | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_PEEK,
mZenModeHelperSpy.mConfig.suppressedVisualEffects);
@@ -1296,7 +1298,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
parser.nextTag();
mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS,
+ assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_LIGHTS
+ | SUPPRESSED_EFFECT_AMBIENT,
mZenModeHelperSpy.mConfig.suppressedVisualEffects);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 32a4774ca4f2..697d1021889e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -260,7 +260,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
notifyActivityLaunching(mTopActivity.intent);
notifyActivityLaunched(START_SUCCESS, mTopActivity);
doReturn(true).when(mTopActivity.mDisplayContent).isSleeping();
- mTopActivity.setState(Task.ActivityState.RESUMED, "test");
+ mTopActivity.setState(ActivityRecord.State.RESUMED, "test");
mTopActivity.setVisibility(false);
waitHandlerIdle(mAtm.mH);
// Not cancel immediately because in one of real cases, the keyguard may be going away or
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 a1f1610d95db..fbe8c89c6da0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -66,19 +66,19 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -136,7 +136,7 @@ import android.window.TaskSnapshot;
import androidx.test.filters.MediumTest;
import com.android.internal.R;
-import com.android.server.wm.Task.ActivityState;
+import com.android.server.wm.ActivityRecord.State;
import org.junit.Assert;
import org.junit.Before;
@@ -171,25 +171,25 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testStackCleanupOnClearingTask() {
+ public void testTaskFragmentCleanupOnClearingTask() {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
- final Task rootTask = activity.getRootTask();
+ final TaskFragment taskFragment = activity.getTaskFragment();
activity.onParentChanged(null /*newParent*/, task);
- verify(rootTask, times(1)).cleanUpActivityReferences(any());
+ verify(taskFragment).cleanUpActivityReferences(any());
}
@Test
- public void testStackCleanupOnActivityRemoval() {
+ public void testTaskFragmentCleanupOnActivityRemoval() {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
- final Task rootTask = activity.getRootTask();
+ final TaskFragment taskFragment = activity.getTaskFragment();
task.removeChild(activity);
- verify(rootTask, times(1)).cleanUpActivityReferences(any());
+ verify(taskFragment).cleanUpActivityReferences(any());
}
@Test
- public void testStackCleanupOnTaskRemoval() {
+ public void testRootTaskCleanupOnTaskRemoval() {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
final Task rootTask = activity.getRootTask();
@@ -344,7 +344,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetsRelaunchReason_NotDragResizing() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -369,7 +369,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetsRelaunchReason_DragResizing() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -396,7 +396,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testRelaunchClearTopWaitingTranslucent() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -417,7 +417,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetsRelaunchReason_NonResizeConfigChanges() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -465,7 +465,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.setCreateTask(true)
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
activity.getConfiguration()));
@@ -628,7 +628,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testShouldMakeActive_deferredResume() {
final ActivityRecord activity = createActivityWithTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
mSupervisor.beginDeferResume();
assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
@@ -644,7 +644,7 @@ public class ActivityRecordTests extends WindowTestsBase {
ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
finishingActivity.finishing = true;
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
}
@@ -653,15 +653,16 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testShouldResume_stackVisibility() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
- doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT)
+ .when(task).getVisibility(null);
assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
}
@@ -669,13 +670,13 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testShouldResumeOrPauseWithResults() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
topActivity.finishing = true;
- doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */));
}
@@ -688,7 +689,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
@@ -731,7 +732,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = createActivityWithTask();
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
topActivity.setOccludesParent(false);
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
activity.setVisibility(true);
activity.makeActiveIfNeeded(null /* activeActivity */);
assertEquals(STARTED, activity.getState());
@@ -1015,8 +1016,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
final ActivityRecord activity = createActivityWithTask();
- final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
- for (ActivityState state : states) {
+ final State[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+ for (State state : states) {
activity.finishing = false;
activity.setState(state, "test");
reset(activity);
@@ -1151,7 +1152,7 @@ public class ActivityRecordTests extends WindowTestsBase {
/**
* Verify that finish request won't change the state of next top activity if the current
* finishing activity doesn't need to be destroyed immediately. The case is usually like
- * from {@link ActivityStack#completePauseLocked(boolean, ActivityRecord)} to
+ * from {@link Task#completePause(boolean, ActivityRecord)} to
* {@link ActivityRecord#completeFinishing(String)}, so the complete-pause should take the
* responsibility to resume the next activity with updating the state.
*/
@@ -1397,7 +1398,7 @@ public class ActivityRecordTests extends WindowTestsBase {
}
private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask,
- ActivityState secondActivityState) {
+ State secondActivityState) {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1449,7 +1450,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testDestroyIfPossible() {
final ActivityRecord activity = createActivityWithTask();
- doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+ doReturn(false).when(mRootWindowContainer)
+ .resumeFocusedTasksTopActivities();
activity.destroyIfPossible("test");
assertEquals(DESTROYING, activity.getState());
@@ -1471,7 +1473,8 @@ public class ActivityRecordTests extends WindowTestsBase {
homeStack.removeChild(t, "test");
}, true /* traverseTopToBottom */);
activity.finishing = true;
- doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+ doReturn(false).when(mRootWindowContainer)
+ .resumeFocusedTasksTopActivities();
// Try to destroy the last activity above the home stack.
activity.destroyIfPossible("test");
@@ -1907,8 +1910,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(wpc.registeredForActivityConfigChanges());
// Create a new task with custom config to reparent the activity to.
- final Task newTask =
- new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+ final Task newTask = new TaskBuilder(mSupervisor).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1940,8 +1942,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.diff(wpc.getRequestedOverrideConfiguration()));
// Create a new task with custom config to reparent the second activity to.
- final Task newTask =
- new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+ final Task newTask = new TaskBuilder(mSupervisor).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -2151,7 +2152,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertFalse(activity.supportsPictureInPicture());
}
- private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state,
+ private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
boolean shouldUpdate, boolean activityChange) {
reset(activity.app);
activity.setState(state, "test");
@@ -2595,7 +2596,8 @@ public class ActivityRecordTests extends WindowTestsBase {
any(), any(), anyBoolean(), anyBoolean(), any(), any());
// In normal case, resumeFocusedTasksTopActivities() should be called after
// startActivityLocked(). So skip resumeFocusedTasksTopActivities() in ActivityBuilder.
- doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+ doReturn(false).when(mRootWindowContainer)
+ .resumeFocusedTasksTopActivities();
// Make mVisibleSetFromTransferredStartingWindow true.
final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
task.startActivityLocked(middle, null /* focusedTopActivity */,
@@ -2738,6 +2740,40 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
+ public void testCloseToSquareFixedOrientationPortrait() {
+ // create a square display
+ final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+ .setSystemDecorations(true).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+ // create a fixed portrait activity
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+
+ // check that both the configuration and app bounds are portrait
+ assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
+ assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+ <= activity.getConfiguration().windowConfiguration.getAppBounds().height());
+ }
+
+ @Test
+ public void testCloseToSquareFixedOrientationLandscape() {
+ // create a square display
+ final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+ .setSystemDecorations(true).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+ // create a fixed landscape activity
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE).build();
+
+ // check that both the configuration and app bounds are landscape
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+ assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+ > activity.getConfiguration().windowConfiguration.getAppBounds().height());
+ }
+
+ @Test
public void testSetVisibility_visibleToVisible() {
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index d0588a30ca67..2df9a8df3a99 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1136,6 +1136,7 @@ public class ActivityStarterTests extends WindowTestsBase {
/* doResume */true,
/* options */null,
/* inTask */null,
+ /* inTaskFragment */ null,
/* restrictedBgActivity */false,
/* intentGrants */null);
@@ -1146,6 +1147,31 @@ public class ActivityStarterTests extends WindowTestsBase {
}
@Test
+ public void testStartActivityInner_inTaskFragment() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+ final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+ true /* createdByOrganizer */);
+ sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+ starter.startActivityInner(
+ /* r */targetRecord,
+ /* sourceRecord */ sourceRecord,
+ /* voiceSession */null,
+ /* voiceInteractor */ null,
+ /* startFlags */ 0,
+ /* doResume */true,
+ /* options */null,
+ /* inTask */null,
+ /* inTaskFragment */ taskFragment,
+ /* restrictedBgActivity */false,
+ /* intentGrants */null);
+
+ assertTrue(taskFragment.hasChild());
+ }
+
+ @Test
public void testLaunchCookie_newAndExistingTask() {
final ActivityStarter starter = prepareStarter(0, false);
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 741f33f8aca7..8ca14bc0bb86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,6 +26,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -244,7 +248,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
.setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask())
.build();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
mSupervisor.endDeferResume();
assertEquals(activity.app, mAtm.mInternal.getTopApp());
@@ -254,13 +258,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
activity.mVisibleRequested = false;
activity.setVisible(false);
activity.getTask().setPausingActivity(activity);
- homeActivity.setState(Task.ActivityState.PAUSED, "test");
+ homeActivity.setState(PAUSED, "test");
// Even the visibility states are invisible, the next activity should be resumed because
// the crashed activity was pausing.
mAtm.mInternal.handleAppDied(activity.app, false /* restarting */,
null /* finishInstrumentationCallback */);
- assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
+ assertEquals(RESUMED, homeActivity.getState());
assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
}
@@ -271,7 +275,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
final Task rootHomeTask = mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mAtm).setTask(rootHomeTask).build();
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- topActivity.setState(Task.ActivityState.RESUMED, "test");
+ topActivity.setState(RESUMED, "test");
final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
assertFalse(mAtm.mInternal.isSleeping());
@@ -287,7 +291,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
verify(mSupervisor.mGoingToSleepWakeLock).acquire();
doReturn(true).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
- assertEquals(Task.ActivityState.PAUSING, topActivity.getState());
+ assertEquals(PAUSING, topActivity.getState());
assertTrue(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
mAtm.mInternal.getTopProcessState());
@@ -298,7 +302,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
final Task topRootTask = topActivity.getRootTask();
doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean());
doReturn(true).when(topRootTask).goToSleepIfPossible(anyBoolean());
- topActivity.setState(Task.ActivityState.STOPPING, "test");
+ topActivity.setState(STOPPING, "test");
topActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */,
null /* description */);
verify(mSupervisor.mGoingToSleepWakeLock).release();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 31d46125fd70..af21e02ce27c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -35,10 +35,10 @@ import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
-import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index d5628fc9de48..c4faaa31e5a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -61,6 +61,7 @@ import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
import com.google.android.collect.Lists;
@@ -566,6 +567,31 @@ public class DisplayAreaTest extends WindowTestsBase {
.onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea);
}
+ @Test
+ public void testGetDisplayAreaInfo() {
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+ mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
+ mDisplayContent.addChild(displayArea, 0);
+ final DisplayAreaInfo info = displayArea.getDisplayAreaInfo();
+
+ assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken());
+ assertThat(info.configuration).isEqualTo(displayArea.getConfiguration());
+ assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId());
+ assertThat(info.featureId).isEqualTo(displayArea.mFeatureId);
+ assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId);
+
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ final int tdaIndex = tda.getParent().mChildren.indexOf(tda);
+ final RootDisplayArea root =
+ new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1);
+ mDisplayContent.addChild(root, tdaIndex + 1);
+ displayArea.reparent(root, 0);
+
+ final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo();
+
+ assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId);
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");
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 d498d4663d78..fb71dcbbdaee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1528,7 +1528,7 @@ public class DisplayContentTests extends WindowTestsBase {
unblockDisplayRotation(mDisplayContent);
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
app.setVisible(false);
- app.setState(Task.ActivityState.RESUMED, "test");
+ app.setState(ActivityRecord.State.RESUMED, "test");
mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 03304bb9456a..3741d499bc07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -24,7 +24,7 @@ import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
@@ -50,13 +50,13 @@ import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.expectThrows;
import android.graphics.Insets;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -69,7 +69,6 @@ import android.view.PrivacyIndicatorBounds;
import android.view.RoundedCorners;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
-import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -109,12 +108,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
// We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
// changing those frames.
- doNothing().when(mWindow).computeFrame();
-
- final WindowManager.LayoutParams attrs = mWindow.mAttrs;
- attrs.width = MATCH_PARENT;
- attrs.height = MATCH_PARENT;
- attrs.format = PixelFormat.TRANSLUCENT;
+ doNothing().when(mWindow).computeFrame(any());
spyOn(mStatusBarWindow);
spyOn(mNavBarWindow);
@@ -219,7 +213,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
- public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() {
+ public void addingWindow_InWindowTypeWithPredefinedInsets() {
mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
@@ -230,7 +224,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
InsetsSourceProvider provider =
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
- assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ // In the new flexible insets setup, the insets frame should always respect the window
+ // layout result.
+ assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ } else {
+ assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ }
}
@Test
@@ -733,10 +733,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void testFixedRotationInsetsSourceFrame() {
+ mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT;
+ mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH;
doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
.rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
- mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
- .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
+ mWindow.mAboveInsetsState.set(
+ mDisplayContent.getInsetsStateController().getRawInsetsState());
final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
.getSource(ITYPE_STATUS_BAR).getFrame();
mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index b793be74c033..70aa2a2f32bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -37,7 +37,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT;
import static org.junit.Assert.assertEquals;
@@ -107,8 +106,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
@Test
public void testChooseNavigationColorWindowLw() {
- final WindowState opaque = createOpaqueFullscreen(false);
-
+ final WindowState candidate = createOpaqueFullscreen(false);
final WindowState dimmingImTarget = createDimmingDialogWindow(true);
final WindowState dimmingNonImTarget = createDimmingDialogWindow(false);
@@ -116,45 +114,51 @@ public class DisplayPolicyTests extends WindowTestsBase {
final WindowState invisibleIme = createInputMethodWindow(false, true, false);
final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
- // If everything is null, return null
+ // If everything is null, return null.
assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw(
- null, null, null, NAV_BAR_BOTTOM));
+ null, null, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, null, NAV_BAR_BOTTOM));
+ // If no IME windows, return candidate window.
+ assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, null, NAV_BAR_BOTTOM));
assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+ dimmingImTarget, null, NAV_BAR_BOTTOM));
assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, null, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- null, null, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ // If IME is not visible, return candidate window.
+ assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ dimmingImTarget, invisibleIme, NAV_BAR_BOTTOM));
assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, invisibleIme, NAV_BAR_BOTTOM));
+
+ // If IME is visible, return candidate when the candidate window is not dimming.
assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
+ null, visibleIme, NAV_BAR_BOTTOM));
assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ candidate, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+ // If IME is visible and the candidate window is dimming, checks whether the dimming window
+ // can be IME tartget or not.
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
// Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
// window.
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, imeNonDrawNavBar, NAV_BAR_BOTTOM));
assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
}
@UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
@@ -182,59 +186,32 @@ public class DisplayPolicyTests extends WindowTestsBase {
// If there is no window, APPEARANCE_LIGHT_NAVIGATION_BARS is not allowed.
assertEquals(0,
- displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, null, null,
- null, null));
-
- // Opaque top fullscreen window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, opaqueDarkNavBar, null,
- opaqueDarkNavBar));
- assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
- displayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
- opaqueLightNavBar, null, opaqueLightNavBar));
- assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
- displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS,
- opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+ displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null));
// Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, dimming));
assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- 0, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- 0, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, imeDrawLightNavBar,
- dimming));
+ APPEARANCE_LIGHT_NAVIGATION_BARS, dimming));
- // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS
+ // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar));
assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar,
- imeDrawDarkNavBar));
-
- // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins.
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, opaqueLightNavBar,
- imeDrawDarkNavBar, imeDrawDarkNavBar));
-
- // IME window should be able to use APPEARANCE_LIGHT_NAVIGATION_BARS.
- assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
- displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
- opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar));
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
+ 0, opaqueLightNavBar));
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar));
}
- @UseTestDisplay(addWindows = W_ACTIVITY)
+ @UseTestDisplay(addWindows = {W_ACTIVITY, W_STATUS_BAR})
@Test
public void testComputeTopFullscreenOpaqueWindow() {
final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
attrs.x = attrs.y = 0;
attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT;
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ policy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
+
policy.applyPostLayoutPolicyLw(
mAppWindow, attrs, null /* attached */, null /* imeTarget */);
@@ -313,7 +290,9 @@ public class DisplayPolicyTests extends WindowTestsBase {
displayInfo.logicalHeight = 2000;
displayInfo.rotation = ROTATION_0;
- displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+ WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
+ displayPolicy.addWindowLw(mNavBarWindow, attrs);
+ mNavBarWindow.setRequestedSize(attrs.width, attrs.height);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
mImeWindow.mAboveInsetsState.set(state);
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 683ed889d283..3982a83d7778 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -58,8 +58,6 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
static final int DISPLAY_HEIGHT = 1000;
static final int DISPLAY_DENSITY = 320;
- static final int STATUS_BAR_HEIGHT = 10;
- static final int NAV_BAR_HEIGHT = 15;
static final int DISPLAY_CUTOUT_HEIGHT = 8;
static final int IME_HEIGHT = 415;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f2418c68358d..a8ede13e5de6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -82,7 +82,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
- return true;
+ return dc.getImeContainer().getConfiguration();
}).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
anyInt(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index bf3ed692dc8e..5b04c91d5a63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -80,7 +81,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
- public void testControlsForDispatch_dockedStackVisible() {
+ public void testControlsForDispatch_dockedTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -93,25 +94,26 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
- public void testControlsForDispatch_freeformStackVisible() {
+ public void testControlsForDispatch_multiWindowTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
- final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+ final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
- // The app must not control any bars.
+ // The app must not control any system bars.
assertNull(controls);
}
@Test
- public void testControlsForDispatch_dockedDividerControllerResizing() {
+ public void testControlsForDispatch_freeformTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
- mDisplayContent.getDockedDividerController().setResizing(true);
- final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+ final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+ ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
// The app must not control any system bars.
assertNull(controls);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 647a898a5361..609d15937b2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -200,28 +200,37 @@ public class LetterboxTest {
assertTrue(mLetterbox.needsApplySurfaceChanges());
mLetterbox.applySurfaceChanges(mTransaction);
- verify(mTransaction).setAlpha(mSurfaces.top, mDarkScrimAlpha);
+ verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
}
@Test
- public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
+ public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
- assertNull(mSurfaces.behind);
+ assertNull(mSurfaces.fullWindowSurface);
}
@Test
- public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() {
+ public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
- assertNotNull(mSurfaces.behind);
+ assertNotNull(mSurfaces.fullWindowSurface);
}
@Test
- public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() {
+ public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
+ mHasWallpaperBackground = true;
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertNotNull(mSurfaces.fullWindowSurface);
+ }
+
+ @Test
+ public void testNotIntersectsOrFullyContains_cornersRounded() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
mLetterbox.applySurfaceChanges(mTransaction);
@@ -249,8 +258,8 @@ public class LetterboxTest {
public SurfaceControl right;
private SurfaceControl.Builder mBottomBuilder;
public SurfaceControl bottom;
- private SurfaceControl.Builder mBehindBuilder;
- public SurfaceControl behind;
+ private SurfaceControl.Builder mFullWindowSurfaceBuilder;
+ public SurfaceControl fullWindowSurface;
@Override
public SurfaceControl.Builder get() {
@@ -265,8 +274,8 @@ public class LetterboxTest {
mRightBuilder = (SurfaceControl.Builder) i.getMock();
} else if (((String) i.getArgument(0)).contains("bottom")) {
mBottomBuilder = (SurfaceControl.Builder) i.getMock();
- } else if (((String) i.getArgument(0)).contains("behind")) {
- mBehindBuilder = (SurfaceControl.Builder) i.getMock();
+ } else if (((String) i.getArgument(0)).contains("fullWindow")) {
+ mFullWindowSurfaceBuilder = (SurfaceControl.Builder) i.getMock();
}
return i.getMock();
});
@@ -281,8 +290,8 @@ public class LetterboxTest {
right = control;
} else if (i.getMock() == mBottomBuilder) {
bottom = control;
- } else if (i.getMock() == mBehindBuilder) {
- behind = control;
+ } else if (i.getMock() == mFullWindowSurfaceBuilder) {
+ fullWindowSurface = control;
}
return control;
}).when(builder).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index d017c19cac86..1b19a28a9790 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -31,8 +31,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 0c6545c2438f..7ebf7755ee4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -36,22 +36,23 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -280,11 +281,11 @@ public class RootTaskTests extends WindowTestsBase {
public void testResumedActivity() {
final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task task = r.getTask();
- assertNull(task.getResumedActivity());
+ assertNull(task.getTopResumedActivity());
r.setState(RESUMED, "testResumedActivity");
- assertEquals(r, task.getResumedActivity());
+ assertEquals(r, task.getTopResumedActivity());
r.setState(PAUSING, "testResumedActivity");
- assertNull(task.getResumedActivity());
+ assertNull(task.getTopResumedActivity());
}
@Test
@@ -295,15 +296,15 @@ public class RootTaskTests extends WindowTestsBase {
final Task task = r.getTask();
// Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
- assertEquals(r, rootTask.getResumedActivity());
+ assertEquals(r, rootTask.getTopResumedActivity());
final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
false /* animate */, true /* deferResume*/,
"testResumedActivityFromTaskReparenting");
- assertNull(rootTask.getResumedActivity());
- assertEquals(r, destRootTask.getResumedActivity());
+ assertNull(rootTask.getTopResumedActivity());
+ assertEquals(r, destRootTask.getTopResumedActivity());
}
@Test
@@ -314,15 +315,15 @@ public class RootTaskTests extends WindowTestsBase {
final Task task = r.getTask();
// Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
- assertEquals(r, rootTask.getResumedActivity());
+ assertEquals(r, rootTask.getTopResumedActivity());
final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
false /* animate */, false /* deferResume*/,
"testResumedActivityFromActivityReparenting");
- assertNull(rootTask.getResumedActivity());
- assertEquals(r, destRootTask.getResumedActivity());
+ assertNull(rootTask.getTopResumedActivity());
+ assertEquals(r, destRootTask.getTopResumedActivity());
}
@Test
@@ -618,9 +619,9 @@ public class RootTaskTests extends WindowTestsBase {
doReturn(false).when(splitScreenSecondary2).isTranslucent(any());
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// First split-screen secondary should be visible behind another translucent split-screen
@@ -628,9 +629,9 @@ public class RootTaskTests extends WindowTestsBase {
doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
@@ -642,13 +643,13 @@ public class RootTaskTests extends WindowTestsBase {
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// Split-screen root tasks should be visible behind a translucent fullscreen root task.
@@ -657,13 +658,13 @@ public class RootTaskTests extends WindowTestsBase {
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary2.getVisibility(null /* starting */));
// Assistant root task shouldn't be visible behind translucent split-screen root task,
@@ -678,25 +679,25 @@ public class RootTaskTests extends WindowTestsBase {
assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
} else {
assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
}
}
@@ -707,45 +708,51 @@ public class RootTaskTests extends WindowTestsBase {
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ // Creating as two-level tasks so home task can be reparented to split-secondary root task.
final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */,
+ true /* twoLevelTask */);
doReturn(false).when(homeRootTask).isTranslucent(any());
doReturn(false).when(splitPrimary).isTranslucent(any());
doReturn(false).when(splitSecondary).isTranslucent(any());
-
// Re-parent home to split secondary.
homeRootTask.reparent(splitSecondary, POSITION_TOP);
// Current tasks should be visible.
- assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ splitPrimary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ splitSecondary.getVisibility(null /* starting */));
// Home task should still be visible even though it is a child of another visible task.
- assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ homeRootTask.getVisibility(null /* starting */));
// Add fullscreen translucent task that partially occludes split tasks
final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
WINDOWING_MODE_FULLSCREEN, true /* translucent */);
// Fullscreen translucent task should be visible
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
// Split tasks should be visible behind translucent
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitSecondary.getVisibility(null /* starting */));
// Home task should be visible behind translucent since its parent is visible behind
// translucent.
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
homeRootTask.getVisibility(null /* starting */));
// Hide split-secondary
splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
// Home split secondary and home task should be invisible.
- assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ homeRootTask.getVisibility(null /* starting */));
}
@Test
@@ -757,9 +764,9 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -775,10 +782,12 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
translucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ opaqueRootTask.getVisibility(null /* starting */));
}
@Test
@@ -793,10 +802,11 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
opaqueRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -809,9 +819,9 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomTranslucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -824,9 +834,10 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
bottomTranslucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ opaqueRootTask.getVisibility(null /* starting */));
}
@Test
@@ -840,16 +851,17 @@ public class RootTaskTests extends WindowTestsBase {
final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
// Add an activity to the pinned root task so it isn't considered empty for visibility
// check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
.setTask(pinnedRootTask)
.build();
- assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ pinnedRootTask.getVisibility(null /* starting */));
}
@Test
@@ -1142,12 +1154,12 @@ public class RootTaskTests extends WindowTestsBase {
} else if (twoLevelTask) {
task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(taskDisplayArea)
- .setWindowingMode(windowingMode)
.setActivityType(activityType)
.setOnTop(onTop)
.setCreateActivity(true)
.setCreateParentTask(true)
.build().getRootTask();
+ task.setWindowingMode(windowingMode);
} else {
task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(taskDisplayArea)
@@ -1301,9 +1313,9 @@ public class RootTaskTests extends WindowTestsBase {
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
- task.startPausingLocked(false /* uiSleeping */, topActivity,
+ task.startPausing(false /* uiSleeping */, topActivity,
"test");
- verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
+ verify(task).completePause(anyBoolean(), eq(topActivity));
}
@Test
@@ -1544,7 +1556,7 @@ public class RootTaskTests extends WindowTestsBase {
activities[i] = r;
doReturn(null).when(mAtm).getProcessController(
eq(r.processName), eq(r.info.applicationInfo.uid));
- r.setState(Task.ActivityState.INITIALIZING, "test");
+ r.setState(INITIALIZING, "test");
// Ensure precondition that the activity is opaque.
assertTrue(r.occludesParent());
mSupervisor.startSpecificActivity(r, false /* andResume */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9cf29d4dcc50..8d4acbbac993 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -31,13 +31,14 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.google.common.truth.Truth.assertThat;
@@ -365,7 +366,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask();
mRootWindowContainer.applySleepTokens(true);
- verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+ verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping();
verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
null /* target */, null /* targetOptions */);
}
@@ -386,7 +387,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
// landscape and the portrait lockscreen is shown.
activity.setLastReportedConfiguration(
new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
- activity.setState(Task.ActivityState.STOPPED, "sleep");
+ activity.setState(STOPPED, "sleep");
display.setIsSleeping(true);
doReturn(false).when(display).shouldSleep();
@@ -557,8 +558,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
// Use the task as target to resume.
- mRootWindowContainer.resumeFocusedTasksTopActivities(
- rootTask, activity, null /* targetOptions */);
+ mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity,
+ null /* targetOptions */);
// Verify the target task should resume its activity.
verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
@@ -626,7 +627,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(rootTask).setOnTop(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
// Assume the task is at the topmost position
assertTrue(rootTask.isTopRootTaskInDisplayArea());
@@ -646,7 +647,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(rootTask).setOnTop(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
// Assume the task is at the topmost position
@@ -774,7 +775,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
}
/**
- * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+ * Tests that when starting {@link ResolverActivity} for home, it should use the standard
* activity type (in a new root task) so the order of back stack won't be broken.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index f35e85c3b14c..59894973521d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,8 +40,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -130,7 +132,7 @@ public class SizeCompatTests extends WindowTestsBase {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
mActivity.mVisibleRequested = true;
mActivity.setSavedState(null /* savedState */);
- mActivity.setState(Task.ActivityState.RESUMED, "testRestart");
+ mActivity.setState(RESUMED, "testRestart");
prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
@@ -138,7 +140,7 @@ public class SizeCompatTests extends WindowTestsBase {
// The visible activity should recompute configuration according to the last parent bounds.
mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken);
- assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
+ assertEquals(RESTARTING_PROCESS, mActivity.getState());
assertNotEquals(originalOverrideBounds, mActivity.getBounds());
}
@@ -585,7 +587,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testHandleActivitySizeCompatModeChanged() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -604,7 +606,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mVisibleRequested = true;
mActivity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity);
// Expect null token when switching to non-size-compat mode activity.
@@ -618,7 +620,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testHandleActivitySizeCompatModeChangedOnDifferentTask() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -637,7 +639,7 @@ public class SizeCompatTests extends WindowTestsBase {
.setCreateActivity(true).build();
final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity();
doReturn(true).when(secondTask).isOrganized();
- secondActivity.setState(Task.ActivityState.RESUMED,
+ secondActivity.setState(RESUMED,
"testHandleActivitySizeCompatModeChanged");
prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -1553,6 +1555,30 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
+ // Set up a display in landscape with an unresizable app.
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */);
+ prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
+ assertFitted();
+
+ // Activity max bounds not be sandboxed since sandboxing is disabled.
+ assertMaxBoundsInheritDisplayAreaBounds();
+ }
+
+ @Test
+ public void testSandboxDisplayApis_unresizableAppSandboxed() {
+ // Set up a display in landscape with an unresizable app.
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */);
+ prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
+ assertFitted();
+
+ // Activity max bounds should be sandboxed since sandboxing is enabled.
+ assertActivityMaxBoundsSandboxed();
+ }
+
+ @Test
public void testResizableApp_notSandboxed() {
// Set up a display in landscape with a fully resizable app.
setUpDisplaySizeWithApp(2500, 1000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b89539cabbfe..e32b2aa68b69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -178,6 +178,11 @@ public class StubTransaction extends SurfaceControl.Transaction {
}
@Override
+ public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) {
+ return this;
+ }
+
+ @Override
public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 67b273a5a82d..c45c18d16c38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -37,8 +37,8 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -84,8 +84,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -111,8 +110,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -133,8 +131,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
new file mode 100644
index 000000000000..6bd8ad27342a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.wm;
+
+import static android.window.TaskFragmentOrganizer.putExceptionInBundle;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest WmTests:TaskFragmentOrganizerControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
+
+ private TaskFragmentOrganizerController mController;
+ private TaskFragmentOrganizer mOrganizer;
+ private ITaskFragmentOrganizer mIOrganizer;
+ private TaskFragment mTaskFragment;
+ private TaskFragmentInfo mTaskFragmentInfo;
+ private IBinder mFragmentToken;
+
+ @Before
+ public void setup() {
+ mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mOrganizer = new TaskFragmentOrganizer(Runnable::run);
+ mIOrganizer = mOrganizer.getIOrganizer();
+ mTaskFragment = mock(TaskFragment.class);
+ mTaskFragmentInfo = mock(TaskFragmentInfo.class);
+ mFragmentToken = new Binder();
+
+ spyOn(mController);
+ spyOn(mOrganizer);
+ doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
+ doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
+ doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
+ doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
+ doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+ }
+
+ @Test
+ public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment));
+ }
+
+ @Test
+ public void testOnTaskFragmentAppeared() {
+ mController.registerOrganizer(mIOrganizer);
+
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+
+ verify(mOrganizer).onTaskFragmentAppeared(any());
+ }
+
+ @Test
+ public void testOnTaskFragmentInfoChanged() {
+ mController.registerOrganizer(mIOrganizer);
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+
+ // No callback if the info is not changed.
+ doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+ doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+ // Trigger callback if the info is changed.
+ doReturn(false).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+
+ verify(mOrganizer).onTaskFragmentInfoChanged(mTaskFragmentInfo);
+ }
+
+ @Test
+ public void testOnTaskFragmentVanished() {
+ mController.registerOrganizer(mIOrganizer);
+
+ mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+
+ verify(mOrganizer).onTaskFragmentVanished(any());
+ }
+
+ @Test
+ public void testOnTaskFragmentParentInfoChanged() {
+ mController.registerOrganizer(mIOrganizer);
+ final Task parent = mock(Task.class);
+ final Configuration parentConfig = new Configuration();
+ parentConfig.smallestScreenWidthDp = 10;
+ doReturn(parent).when(mTaskFragment).getParent();
+ doReturn(parentConfig).when(parent).getConfiguration();
+ doReturn(parent).when(parent).asTask();
+
+ mController.onTaskFragmentParentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+
+ // No extra callback if the info is not changed.
+ clearInvocations(mOrganizer);
+
+ mController.onTaskFragmentParentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
+
+ // Trigger callback if the info is changed.
+ parentConfig.smallestScreenWidthDp = 100;
+
+ mController.onTaskFragmentParentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ }
+
+ @Test
+ public void testOnTaskFragmentError() throws RemoteException {
+ final IBinder errorCallbackToken = new Binder();
+ final Throwable exception = new IllegalArgumentException("Test exception");
+ final Bundle exceptionBundle = putExceptionInBundle(exception);
+
+ mIOrganizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
+
+ verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
+ }
+}
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 0ebff1d253ef..629e45208234 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1257,7 +1257,8 @@ public class TaskTests extends WindowTestsBase {
LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
spyOn(persister);
- final Task task = getTestTask();
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setCreateParentTask(true).build().getRootTask();
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
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 2dfb3a1a84bc..45e5f8e55f8a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -339,6 +339,44 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testTargets_noIntermediatesToWallpaper() {
+ final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ // Make DA organized so we can check that they don't get included.
+ WindowContainer parent = wallpaperWindowToken.getParent();
+ while (parent != null && parent != mDisplayContent) {
+ if (parent.asDisplayArea() != null) {
+ parent.asDisplayArea().setOrganizer(
+ mock(android.window.IDisplayAreaOrganizer.class), true /* skipAppear */);
+ }
+ parent = parent.getParent();
+ }
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ wallpaperWindowToken.setVisibleRequested(false);
+ transition.collect(wallpaperWindowToken);
+ wallpaperWindowToken.setVisibleRequested(true);
+ wallpaperWindow.mHasSurface = true;
+ doReturn(true).when(mDisplayContent).isAttached();
+ transition.collect(mDisplayContent);
+ mDisplayContent.getWindowConfiguration().setRotation(
+ (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
+
+ ArraySet<WindowContainer> targets = Transition.calculateTargets(
+ transition.mParticipants, transition.mChanges);
+ TransitionInfo info = Transition.calculateTransitionInfo(
+ 0, 0, targets, transition.mChanges);
+ // The wallpaper is not organized, so it won't have a token; however, it will be marked
+ // as IS_WALLPAPER
+ assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
+ // Make sure no intermediate display areas were pulled in between wallpaper and display.
+ assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
+ info.getChanges().get(0).getParent());
+ }
+
+ @Test
public void testIndependent() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index a1f89ec75784..efe65381b7df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -57,12 +57,14 @@ import org.mockito.Mockito;
public class WindowFrameTests extends WindowTestsBase {
private DisplayContent mTestDisplayContent;
+ private DisplayFrames mTestDisplayFrames;
@Before
public void setUp() throws Exception {
DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
testDisplayInfo.displayCutout = null;
mTestDisplayContent = createNewDisplay(testDisplayInfo);
+ mTestDisplayFrames = mTestDisplayContent.mDisplayFrames;
}
// Do not use this function directly in the tests below. Instead, use more explicit function
@@ -99,7 +101,7 @@ public class WindowFrameTests extends WindowTestsBase {
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 1000, 1000);
assertRelFrame(w, 0, 0, 1000, 1000);
@@ -108,14 +110,14 @@ public class WindowFrameTests extends WindowTestsBase {
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// Explicit width and height without requested width/height
// gets us nothing.
assertFrame(w, 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
assertFrame(w, 0, 0, 300, 300);
@@ -128,14 +130,14 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
@@ -143,7 +145,7 @@ public class WindowFrameTests extends WindowTestsBase {
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
@@ -153,18 +155,18 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 700, 0, 1000, 300);
assertRelFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 700, 700, 1000, 1000);
assertRelFrame(w, 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 600, 600, 900, 900);
assertRelFrame(w, 600, 600, 900, 900);
}
@@ -191,7 +193,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertEquals(resolvedTaskBounds, w.getFrame());
@@ -204,7 +206,7 @@ public class WindowFrameTests extends WindowTestsBase {
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertEquals(resolvedTaskBounds, w.getFrame());
assertEquals(0, w.getRelativeFrame().left);
assertEquals(0, w.getRelativeFrame().top);
@@ -233,7 +235,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -249,7 +251,7 @@ public class WindowFrameTests extends WindowTestsBase {
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setBounds(null);
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, cf);
}
@@ -285,7 +287,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect winRect = new Rect(200, 200, 300, 500);
task.setBounds(winRect);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, winRect.left, imeFrame.top - winRect.height(), winRect.right, imeFrame.top);
// Now check that it won't get moved beyond the top
@@ -293,7 +295,7 @@ public class WindowFrameTests extends WindowTestsBase {
task.setBounds(winRect);
w.setBounds(winRect);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, winRect.left, 0, winRect.right, winRect.height());
// Now we have status bar. Check that it won't go into the status bar area.
@@ -301,14 +303,14 @@ public class WindowFrameTests extends WindowTestsBase {
statusBarFrame.bottom = 60;
state.getSource(ITYPE_STATUS_BAR).setFrame(statusBarFrame);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, winRect.left, statusBarFrame.bottom, winRect.right,
statusBarFrame.bottom + winRect.height());
// Check that it's moved back without ime insets
state.removeSource(ITYPE_IME);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertEquals(winRect, w.getFrame());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index d9aa871447be..83cdc3ba3ebd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -107,9 +107,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -128,9 +128,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -151,9 +151,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index d6a8401f5b18..39fe952cc199 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,8 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -62,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
@@ -70,7 +70,6 @@ import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -346,7 +345,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testDisplayAreaTransaction() {
removeGlobalMinSizeRestriction();
- final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+ final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
testTransaction(displayArea);
}
@@ -364,7 +363,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
testSetWindowingMode(rootTask);
- final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+ final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
testSetWindowingMode(displayArea);
}
@@ -1256,21 +1255,24 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testStartTasksInTransaction() {
WindowContainerTransaction wct = new WindowContainerTransaction();
- Bundle testOptions = new Bundle();
- testOptions.putInt("test", 20);
+ ActivityOptions testOptions = ActivityOptions.makeBasic();
+ testOptions.setTransientLaunch();
wct.startTask(1, null /* options */);
- wct.startTask(2, testOptions);
- spyOn(mWm.mAtmService);
- doReturn(START_CANCELED).when(mWm.mAtmService).startActivityFromRecents(anyInt(), any());
+ wct.startTask(2, testOptions.toBundle());
+ spyOn(mWm.mAtmService.mTaskSupervisor);
+ doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
+ anyInt(), anyInt(), anyInt(), any());
clearInvocations(mWm.mAtmService);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
- final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(1), bundleCaptor.capture());
- assertTrue(bundleCaptor.getValue().isEmpty());
+ verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+ anyInt(), anyInt(), eq(1), any());
- verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(2), bundleCaptor.capture());
- assertEquals(20, bundleCaptor.getValue().getInt("test"));
+ final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
+ ArgumentCaptor.forClass(SafeActivityOptions.class);
+ verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+ anyInt(), anyInt(), eq(2), optionsCaptor.capture());
+ assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index ed18d26f8448..d3f2d1407a46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -23,6 +23,12 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -311,17 +317,17 @@ public class WindowProcessControllerTests extends WindowTestsBase {
callbackResult[0] = 0;
activity.mVisibleRequested = false;
- activity.setState(Task.ActivityState.PAUSED, "test");
+ activity.setState(PAUSED, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(paused, callbackResult[0]);
callbackResult[0] = 0;
- activity.setState(Task.ActivityState.STOPPING, "test");
+ activity.setState(STOPPING, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(stopping, callbackResult[0]);
callbackResult[0] = 0;
- activity.setState(Task.ActivityState.STOPPED, "test");
+ activity.setState(STOPPED, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(other, callbackResult[0]);
}
@@ -332,25 +338,25 @@ public class WindowProcessControllerTests extends WindowTestsBase {
spyOn(tracker);
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
- activity.setState(Task.ActivityState.STARTED, "test");
+ activity.setState(STARTED, "test");
verify(tracker).onAnyActivityVisible(mWpc);
assertTrue(mWpc.hasVisibleActivities());
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
verify(tracker).onActivityResumedWhileVisible(mWpc);
assertTrue(tracker.hasResumedActivity(mWpc.mUid));
activity.makeFinishingLocked();
- activity.setState(Task.ActivityState.PAUSING, "test");
+ activity.setState(PAUSING, "test");
assertFalse(tracker.hasResumedActivity(mWpc.mUid));
assertTrue(mWpc.hasForegroundActivities());
activity.setVisibility(false);
activity.mVisibleRequested = false;
- activity.setState(Task.ActivityState.STOPPED, "test");
+ activity.setState(STOPPED, "test");
verify(tracker).onAllActivitiesInvisible(mWpc);
assertFalse(mWpc.hasVisibleActivities());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 92b670ed9699..e83db78409b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -946,4 +946,19 @@ public class WindowStateTests extends WindowTestsBase {
assertNotNull(state.peekSource(ITYPE_IME));
assertTrue(state.getSource(ITYPE_IME).isVisible());
}
+
+ @Test
+ public void testRequestedVisibility() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mActivityRecord.setVisible(false);
+ app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+ assertFalse(app.isVisibleRequested());
+
+ // It doesn't have a surface yet, but should still be visible requested.
+ app.setHasSurface(false);
+ app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */);
+
+ assertFalse(app.isVisible());
+ assertTrue(app.isVisibleRequested());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 588089996d6c..050fd80411fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -29,11 +30,13 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Process.SYSTEM_UID;
import static android.view.View.VISIBLE;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -83,10 +86,12 @@ import android.service.voice.IVoiceInteractionSession;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.View;
@@ -132,6 +137,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
DisplayInfo mDisplayInfo = new DisplayInfo();
DisplayContent mDefaultDisplay;
+ static final int STATUS_BAR_HEIGHT = 10;
+ static final int NAV_BAR_HEIGHT = 15;
+
/**
* It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with
* {@link UseTestDisplay}, it will be an additional display.
@@ -268,6 +276,14 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) {
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT;
+ mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
+ mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mStatusBarWindow.setRequestedSize(WindowManager.LayoutParams.MATCH_PARENT,
+ STATUS_BAR_HEIGHT);
+ }
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE,
@@ -275,6 +291,15 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) {
mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow");
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT;
+ mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
+ mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ mNavBarWindow.mAttrs.paramsForRotation[rot] =
+ getNavBarLayoutParamsForRotation(rot);
+ }
+ }
}
if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) {
mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER,
@@ -302,6 +327,37 @@ class WindowTestsBase extends SystemServiceTestsBase {
waitUntilHandlersIdle();
}
+ private WindowManager.LayoutParams getNavBarLayoutParamsForRotation(int rotation) {
+ int width = WindowManager.LayoutParams.MATCH_PARENT;
+ int height = WindowManager.LayoutParams.MATCH_PARENT;
+ int gravity = Gravity.BOTTOM;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = NAV_BAR_HEIGHT;
+ break;
+ case Surface.ROTATION_90:
+ gravity = Gravity.RIGHT;
+ width = NAV_BAR_HEIGHT;
+ break;
+ case Surface.ROTATION_270:
+ gravity = Gravity.LEFT;
+ width = NAV_BAR_HEIGHT;
+ break;
+ }
+ }
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR);
+ lp.width = width;
+ lp.height = height;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ lp.gravity = gravity;
+ }
+ return lp;
+ }
+
void beforeCreateTestDisplay() {
// Called before display is created.
}
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 9daa0930e73a..6c20e746f35e 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -2,3 +2,5 @@ mwachens@google.com
varunshah@google.com
huiyu@google.com
yamasani@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS \ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index b056de075c36..763159a2b615 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -208,7 +208,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
public boolean isReservedSupported(String volumeUuid, String callingPackage) {
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false)
- || Build.IS_CONTAINER;
+ || Build.IS_ARC;
} else {
return false;
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f24c365237d..b9666437d829 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -239,8 +239,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mHandler.updateState(state);
} else if ("GETPROTOCOL".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory get protocol");
- long elapsedRealtime = SystemClock.elapsedRealtime();
- mHandler.setAccessoryUEventTime(elapsedRealtime);
+ mHandler.setAccessoryUEventTime(SystemClock.elapsedRealtime());
resetAccessoryHandshakeTimeoutHandler();
} else if ("SENDSTRING".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory send string");
@@ -465,6 +464,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
}
+ //TODO It is not clear that this method serves any purpose (at least on Pixel devices)
+ // consider removing
private static void initRndisAddress() {
// configure RNDIS ethernet address based on our serial number using the same algorithm
// we had been previously using in kernel board files
@@ -484,7 +485,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
try {
FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
} catch (IOException e) {
- Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+ Slog.i(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
}
}
@@ -760,9 +761,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
private void broadcastUsbAccessoryHandshake() {
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long accessoryHandShakeEnd = elapsedRealtime;
-
// send a sticky broadcast containing USB accessory handshake information
Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
.putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
@@ -772,7 +770,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
.putExtra(UsbManager.EXTRA_ACCESSORY_START,
mStartAccessory)
.putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
- accessoryHandShakeEnd);
+ SystemClock.elapsedRealtime());
if (DEBUG) {
Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
@@ -780,7 +778,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
sendStickyBroadcast(intent);
resetUsbAccessoryHandshakeDebuggingInfo();
- accessoryHandShakeEnd = 0L;
}
protected void updateUsbStateBroadcastIfNeeded(long functions) {
@@ -1935,14 +1932,14 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
- Slog.e(TAG, "prcessing MSG_GET_CURRENT_USB_FUNCTIONS");
+ Slog.i(TAG, "processing MSG_GET_CURRENT_USB_FUNCTIONS");
mCurrentUsbFunctionsReceived = true;
if (mCurrentUsbFunctionsRequested) {
- Slog.e(TAG, "updating mCurrentFunctions");
+ Slog.i(TAG, "updating mCurrentFunctions");
// Mask out adb, since it is stored in mAdbEnabled
mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB;
- Slog.e(TAG,
+ Slog.i(TAG,
"mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
mCurrentFunctionsApplied = msg.arg1 == 1;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index a3b5fc7ba872..be37a9139bb4 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -36,7 +36,6 @@ import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
-import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
import android.os.Binder;
import android.os.DeadObjectException;
@@ -109,9 +108,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private boolean mCallActive = false;
private @SoundTriggerPowerSaveMode int mSoundTriggerPowerSaveMode =
PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
- // Indicates if the native sound trigger service is disabled or not.
- // This is an indirect indication of the microphone being open in some other application.
- private boolean mServiceDisabled = false;
// Whether ANY recognition (keyphrase or generic) has been requested.
private boolean mRecognitionRequested = false;
@@ -862,23 +858,19 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
@Override
- public void onSoundModelUpdate(SoundModelEvent event) {
- if (event == null) {
- Slog.w(TAG, "Invalid sound model event!");
- return;
- }
- if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
+ public void onModelUnloaded(int modelHandle) {
+ if (DBG) Slog.d(TAG, "onModelUnloaded: " + modelHandle);
synchronized (mLock) {
MetricsLogger.count(mContext, "sth_sound_model_updated", 1);
- onSoundModelUpdatedLocked(event);
+ onModelUnloadedLocked(modelHandle);
}
}
@Override
- public void onServiceStateChange(int state) {
- if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
+ public void onResourcesAvailable() {
+ if (DBG) Slog.d(TAG, "onResourcesAvailable");
synchronized (mLock) {
- onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
+ onResourcesAvailableLocked();
}
}
@@ -910,15 +902,14 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
updateAllRecognitionsLocked();
}
- private void onSoundModelUpdatedLocked(SoundModelEvent event) {
- // TODO: Handle sound model update here.
+ private void onModelUnloadedLocked(int modelHandle) {
+ ModelData modelData = getModelDataForLocked(modelHandle);
+ if (modelData != null) {
+ modelData.setNotLoaded();
+ }
}
- private void onServiceStateChangedLocked(boolean disabled) {
- if (disabled == mServiceDisabled) {
- return;
- }
- mServiceDisabled = disabled;
+ private void onResourcesAvailableLocked() {
updateAllRecognitionsLocked();
}
@@ -1039,7 +1030,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (mModule != null) {
mModule.detach();
mModule = null;
- mServiceDisabled = false;
}
}
}
@@ -1114,8 +1104,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
pw.print(" call active=");
pw.println(mCallActive);
pw.println(" SoundTrigger Power State=" + mSoundTriggerPowerSaveMode);
- pw.print(" service disabled=");
- pw.println(mServiceDisabled);
}
}
@@ -1329,8 +1317,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode();
}
- return !mCallActive && !mServiceDisabled
- && isRecognitionAllowedByPowerState(
+ return !mCallActive && isRecognitionAllowedByPowerState(
modelData);
}
@@ -1571,6 +1558,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mModelState = MODEL_LOADED;
}
+ synchronized void setNotLoaded() {
+ mModelState = MODEL_NOTLOADED;
+ }
+
synchronized boolean isModelStarted() {
return mModelState == MODEL_STARTED;
}
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index 1963ff3cabc7..391dce1772f1 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -23,6 +23,46 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelephonySdk28TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony2TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony3TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSimRestrictedApisTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonyProviderTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"presubmit-large": [
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 759afd72d539..30fd52814685 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -775,6 +775,21 @@ public abstract class Connection extends Conferenceable {
"android.telecom.extra.REMOTE_PHONE_ACCOUNT_HANDLE";
/**
+ * The Telecom call ID of the conference an existing connection should be added to. This is
+ * required when {@link com.android.services.telephony.TelephonyConnectionService} adds a
+ * {@link Conference} to Telecom using the
+ * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection, Conference)}
+ * API. That API specifies a parent conference associated with the new existing connection
+ * being added, and there is no equivalent as part of the {@link RemoteConnectionService} API.
+ * This extra key is used to stack the ID of the conference to which the existing connection
+ * will be added so that Telecom can link it up correctly when the {@link RemoteConference}
+ * is added to Telecom by the connection manager.
+ * @hide
+ */
+ public static final String EXTRA_ADD_TO_CONFERENCE_ID =
+ "android.telecom.extra.ADD_TO_CONFERENCE_ID";
+
+ /**
* Extra key set from a {@link ConnectionService} when using the remote connection APIs
* (e.g. {@link RemoteConnectionService#createRemoteConnection(PhoneAccountHandle,
* ConnectionRequest, boolean)}) to create a remote connection. Provides the receiving
@@ -1765,11 +1780,13 @@ public abstract class Connection extends Conferenceable {
public abstract void onSetDeviceOrientation(int rotation);
/**
- * Sets camera zoom ratio.
+ * Sets the camera zoom ratio.
* <p>
* Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
*
- * @param value The camera zoom ratio.
+ * @param value The camera zoom ratio; for the current camera, should be a value in the
+ * range defined by
+ * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
*/
public abstract void onSetZoom(float value);
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index f20ee7e56d05..c365648db8f8 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -764,11 +764,13 @@ public abstract class InCallService extends Service {
public abstract void setDeviceOrientation(int rotation);
/**
- * Sets camera zoom ratio.
+ * Sets the camera zoom ratio.
* <p>
* Handled by {@link Connection.VideoProvider#onSetZoom(float)}.
*
- * @param value The camera zoom ratio.
+ * @param value The camera zoom ratio; for the current camera, should be a value in the
+ * range defined by
+ * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
*/
public abstract void setZoom(float value);
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index e1bcb5fbdf00..e3485deb9080 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -34,7 +34,10 @@ import java.util.Objects;
* <ul>
* <li>The component name of the associated connection service.</li>
* <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
- * component name.</li>
+ * component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ * {@link #getId()} provided does not expose personally identifying information. A
+ * {@link ConnectionService} should use an opaque token as the {@link PhoneAccountHandle}
+ * identifier.</li>
* </ul>
*
* Note: This Class requires a non-null {@link ComponentName} and {@link UserHandle} to operate
@@ -49,12 +52,35 @@ public final class PhoneAccountHandle implements Parcelable {
private final String mId;
private final UserHandle mUserHandle;
+ /**
+ * Creates a new {@link PhoneAccountHandle}.
+ *
+ * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
+ * services this {@link PhoneAccountHandle}.
+ * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
+ * component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ * ID provided does not expose personally identifying information. A
+ * {@link ConnectionService} should use an opaque token as the
+ * {@link PhoneAccountHandle} identifier.
+ */
public PhoneAccountHandle(
@NonNull ComponentName componentName,
@NonNull String id) {
this(componentName, id, Process.myUserHandle());
}
+ /**
+ * Creates a new {@link PhoneAccountHandle}.
+ *
+ * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
+ * services this {@link PhoneAccountHandle}.
+ * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
+ * component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ * ID provided does not expose personally identifying information. A
+ * {@link ConnectionService} should use an opaque token as the
+ * {@link PhoneAccountHandle} identifier.
+ * @param userHandle The {@link UserHandle} associated with this {@link PhoneAccountHandle}.
+ */
public PhoneAccountHandle(
@NonNull ComponentName componentName,
@NonNull String id,
@@ -80,17 +106,17 @@ public final class PhoneAccountHandle implements Parcelable {
* others supported by the connection service that created it.
* <p>
* A connection service must select identifiers that are stable for the lifetime of
- * their users' relationship with their service, across many Android devices. For example, a
- * good set of identifiers might be the email addresses with which with users registered for
- * their accounts with a particular service. Depending on how a service chooses to operate,
- * a bad set of identifiers might be an increasing series of integers
- * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
- * collide with values generated on other phones or after a data wipe of a given phone.
- *
+ * their users' relationship with their service, across many Android devices. The identifier
+ * should be a stable opaque token which uniquely identifies the user within the service.
+ * Depending on how a service chooses to operate, a bad set of identifiers might be an
+ * increasing series of integers ({@code 0}, {@code 1}, {@code 2}, ...) that are generated
+ * locally on each phone and could collide with values generated on other phones or after a data
+ * wipe of a given phone.
+ * <p>
* Important: A non-unique identifier could cause non-deterministic call-log backup/restore
* behavior.
*
- * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
+ * @return A service-specific unique opaque identifier for this {@code PhoneAccountHandle}.
*/
public String getId() {
return mId;
@@ -157,7 +183,8 @@ public final class PhoneAccountHandle implements Parcelable {
}
}
- public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR = new Creator<PhoneAccountHandle>() {
+ public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR =
+ new Creator<PhoneAccountHandle>() {
@Override
public PhoneAccountHandle createFromParcel(Parcel in) {
return new PhoneAccountHandle(in);
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bf6a6ef793ff..efe35d21c003 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -239,13 +239,9 @@ final class RemoteConnectionService {
conference.addConnection(c);
}
}
- if (conference.getConnections().size() == 0) {
- // A conference was created, but none of its connections are ones that have been
- // created by, and therefore being tracked by, this remote connection service. It
- // is of no interest to us.
- Log.d(this, "addConferenceCall - skipping");
- return;
- }
+ // We used to skip adding empty conferences; however in the world of IMS conference
+ // calls we need to add them to the remote connection service because they will always
+ // start with no participants.
conference.setState(parcel.getState());
conference.setConnectionCapabilities(parcel.getConnectionCapabilities());
@@ -379,6 +375,8 @@ final class RemoteConnectionService {
@Override
public void addExistingConnection(String callId, ParcelableConnection connection,
Session.Info sessionInfo) {
+ Log.i(RemoteConnectionService.this, "addExistingConnection: callId=%s, conn=%s", callId,
+ connection);
String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
getOpPackageName();
int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo()
@@ -390,6 +388,20 @@ final class RemoteConnectionService {
Bundle newExtras = new Bundle();
newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
connection.getPhoneAccount());
+ if (connection.getParentCallId() != null) {
+ RemoteConference parentConf = mConferenceById.get(connection.getParentCallId());
+ // If there is a parent being set, we need to stash the conference ID here.
+ // Telephony can add an existing connection while specifying a parent conference.
+ // There is no equivalent version of that operation as part of the remote connection
+ // API, so we will stash the pre-defined parent's ID in the extras. When the
+ // connectionmanager copies over the extras from the remote connection to the
+ // actual one, it'll get passed to Telecom so that it can make the association.
+ if (parentConf != null) {
+ newExtras.putString(Connection.EXTRA_ADD_TO_CONFERENCE_ID, parentConf.getId());
+ Log.i(this, "addExistingConnection: stash parent of %s as %s",
+ connection.getParentCallId(), parentConf.getId());
+ }
+ }
remoteConnection.putExtras(newExtras);
mConnectionById.put(callId, remoteConnection);
remoteConnection.registerCallback(new RemoteConnection.Callback() {
diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING
index d58566673eec..02d4eb36221c 100644
--- a/telephony/TEST_MAPPING
+++ b/telephony/TEST_MAPPING
@@ -23,6 +23,46 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelephony2TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonySdk28TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony3TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSimRestrictedApisTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonyProviderTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d745dc215f34..a51b5c146d6b 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1407,7 +1407,7 @@ public class ServiceState implements Parcelable {
m.putString("data-operator-numeric", mOperatorNumeric);
m.putBoolean("manual", mIsManualNetworkSelection);
m.putInt("radioTechnology", getRilVoiceRadioTechnology());
- m.putInt("dataRadioTechnology", getRadioTechnology());
+ m.putInt("dataRadioTechnology", getRilDataRadioTechnology());
m.putBoolean("cssIndicator", mCssIndicator);
m.putInt("networkId", mNetworkId);
m.putInt("systemId", mSystemId);
@@ -1546,17 +1546,6 @@ public class ServiceState implements Parcelable {
}
/**
- * @hide
- * @Deprecated to be removed Q3 2013 use {@link #getRilDataRadioTechnology} or
- * {@link #getRilVoiceRadioTechnology}
- */
- @UnsupportedAppUsage
- public int getRadioTechnology() {
- Rlog.e(LOG_TAG, "ServiceState.getRadioTechnology() DEPRECATED will be removed *******");
- return getRilDataRadioTechnology();
- }
-
- /**
* Transform RIL radio technology {@link RilRadioTechnology} value to Network
* type {@link NetworkType}.
*
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 5171cf9dcea7..73d17100abb2 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2666,7 +2666,8 @@ public final class SmsManager {
* @throws IllegalArgumentException if contentUri is empty
*/
public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
- @Nullable String locationUrl, @Nullable Bundle configOverrides,
+ @Nullable String locationUrl,
+ @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@Nullable PendingIntent sentIntent, long messageId) {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
@@ -2742,7 +2743,8 @@ public final class SmsManager {
* @throws IllegalArgumentException if locationUrl or contentUri is empty
*/
public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
- @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+ @NonNull Uri contentUri,
+ @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@Nullable PendingIntent downloadedIntent, long messageId) {
if (TextUtils.isEmpty(locationUrl)) {
throw new IllegalArgumentException("Empty MMS location URL");
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 88c66acdca6f..f4e2ade643c7 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -79,7 +79,7 @@ public final class TelephonyDisplayInfo implements Parcelable {
* <li>The device is connected to the NR cellular network on millimeter wave bands. </li>
* <li>The device is connected to the specific network which the carrier is using
* proprietary means to provide a faster overall data connection than would be otherwise
- * possible. This may include using other bands unique to the carrier, or carrier
+ * possible. This may include using other bands unique to the carrier, or carrier
* aggregation, for example.</li>
* </ul>
* One of the use case is that UX can show a different icon, for example, "5G+"
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e0ab0a368a4f..8a9e730022c4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8512,13 +8512,13 @@ public class TelephonyManager {
/**
* Get the PLMN chosen for Manual Network Selection if active.
- * Return null string if in automatic selection.
+ * Return empty string if in automatic selection.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
* (see {@link #hasCarrierPrivileges})
*
- * @return manually selected network info on success or null string on failure
+ * @return manually selected network info on success or empty string on failure
*/
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -8531,7 +8531,7 @@ public class TelephonyManager {
} catch (RemoteException ex) {
Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex);
}
- return null;
+ return "";
}
/**
@@ -11115,14 +11115,10 @@ public class TelephonyManager {
@UnsupportedAppUsage
public int getSubIdForPhoneAccount(@Nullable PhoneAccount phoneAccount) {
int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- retval = service.getSubIdForPhoneAccount(phoneAccount);
- }
- } catch (RemoteException e) {
+ if (phoneAccount != null
+ && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+ retval = getSubscriptionId(phoneAccount.getAccountHandle());
}
-
return retval;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 018dabfdb552..4fc777668b66 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1354,11 +1354,6 @@ interface ITelephony {
String callingFeatureId);
/**
- * Returns the subscription ID associated with the specified PhoneAccount.
- */
- int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
-
- /**
* Returns the subscription ID associated with the specified PhoneAccountHandle.
*/
int getSubIdForPhoneAccountHandle(in PhoneAccountHandle phoneAccountHandle,
diff --git a/tests/BatteryStatsPerfTest/AndroidManifest.xml b/tests/BatteryStatsPerfTest/AndroidManifest.xml
index 7633d5283f5e..ab5728e75b9f 100644
--- a/tests/BatteryStatsPerfTest/AndroidManifest.xml
+++ b/tests/BatteryStatsPerfTest/AndroidManifest.xml
@@ -20,6 +20,8 @@
<application>
<uses-library android:name="android.test.runner" />
+ <service android:name="com.android.internal.os.BatteryUsageStatsPerfTest$BatteryUsageStatsService"
+ android:process=":BatteryUsageStatsService" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 54d70478f762..2e4b6da3ba13 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -18,13 +18,25 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import android.app.Service;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.BatteryConsumer;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Binder;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.Parcel;
import android.os.UidBatteryConsumer;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -54,7 +66,8 @@ public class BatteryUsageStatsPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
+ BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(0).build());
state.pauseTiming();
@@ -71,4 +84,118 @@ public class BatteryUsageStatsPerfTest {
state.resumeTiming();
}
}
+
+ private final ConditionVariable mServiceConnected = new ConditionVariable();
+ private IBinder mService;
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = service;
+ mServiceConnected.open();
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ /**
+ * Measures the performance of transferring BatteryUsageStats over a Binder.
+ */
+ @Test
+ public void testBatteryUsageStatsTransferOverBinder() throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ context.bindService(
+ new Intent(context, BatteryUsageStatsService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ mServiceConnected.block(30000);
+ assertThat(mService).isNotNull();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ mService.transact(42, data, reply, 0);
+ final BatteryUsageStats batteryUsageStats =
+ BatteryUsageStats.CREATOR.createFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+
+ state.pauseTiming();
+
+ assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+ assertThat(batteryUsageStats.getUidBatteryConsumers()).hasSize(1000);
+ final UidBatteryConsumer uidBatteryConsumer =
+ batteryUsageStats.getUidBatteryConsumers().get(0);
+ assertThat(uidBatteryConsumer.getConsumedPower(1)).isEqualTo(123);
+
+ state.resumeTiming();
+ }
+
+ context.unbindService(mConnection);
+ }
+
+ /* This service runs in a separate process */
+ public static class BatteryUsageStatsService extends Service {
+ private final BatteryUsageStats mBatteryUsageStats;
+
+ public BatteryUsageStatsService() {
+ mBatteryUsageStats = buildBatteryUsageStats();
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new Binder() {
+ @Override
+ protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
+ int flags) {
+ mBatteryUsageStats.writeToParcel(reply, 0);
+ return true;
+ }
+ };
+ }
+ }
+
+ private static BatteryUsageStats buildBatteryUsageStats() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true)
+ .setBatteryCapacity(4000)
+ .setDischargePercentage(20)
+ .setDischargedPowerRange(1000, 2000)
+ .setStatsStartTimestamp(1000)
+ .setStatsEndTimestamp(3000);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(123)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10300)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+
+ for (int i = 0; i < 1000; i++) {
+ final UidBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(i)
+ .setPackageWithHighestDrain("example.packagename" + i)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
+ }
+
+ consumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+ }
+ return builder.build();
+ }
}
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
index 7ee0d9a5e77e..57303e748738 100644
--- a/tests/BootImageProfileTest/OWNERS
+++ b/tests/BootImageProfileTest/OWNERS
@@ -1,4 +1,4 @@
calin@google.com
-mathieuc@google.com
ngeoffray@google.com
+vmarko@google.com
yawanng@google.com
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 217a72b90fd4..7731e098d9f5 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -25,11 +25,17 @@ package {
android_test {
name: "FlickerTests",
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
test_suites: ["device-tests"],
libs: ["android.test.runner"],
static_libs: [
@@ -46,6 +52,9 @@ android_test {
java_library {
name: "wm-flicker-common-assertions",
platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
srcs: [
"src/**/*Assertions.java",
"src/**/*Assertions.kt",
@@ -56,20 +65,23 @@ java_library {
static_libs: [
"flickerlib",
"truth-prebuilt",
- "app-helpers-core"
+ "app-helpers-core",
],
}
java_library {
name: "wm-flicker-common-app-helpers",
platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
srcs: [
- "**/helpers/*"
+ "**/helpers/*",
],
static_libs: [
"flickerlib",
"flickertestapplib",
"truth-prebuilt",
- "app-helpers-core"
+ "app-helpers-core",
],
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index a540dffb3c9c..08c9e5dc7b64 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -14,77 +14,30 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker
-import android.platform.helpers.IAppHelper
+import android.content.ComponentName
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-val HOME_WINDOW_TITLE = arrayOf("Wallpaper", "Launcher")
+val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
-fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.statusBarWindowIsVisible() {
assertWm {
- this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+ this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
}
}
-fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.navBarWindowIsVisible() {
assertWm {
- this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
- }
-}
-
-fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .showsAppWindowOnTop(*HOME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesVisible() {
- assertWm {
- this.hidesBelowAppWindow(*HOME_WINDOW_TITLE)
- .then()
- .showsBelowAppWindow(*HOME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesInvisible() {
- assertWm {
- this.showsBelowAppWindow(*HOME_WINDOW_TITLE)
- .then()
- .hidesBelowAppWindow(*HOME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) {
- assertWm {
- this.showsAppWindowOnTop(packageName)
- }
-}
-
-fun FlickerTestParameter.appWindowBecomesVisible(appName: String) {
- assertWm {
- this.hidesAppWindow(appName)
- .then()
- .showsAppWindow(appName)
- }
-}
-
-fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) {
- assertWm {
- this.showsAppWindow(appName)
- .then()
- .hidesAppWindow(appName)
+ this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
@JvmOverloads
-fun FlickerTestParameter.noUncoveredRegions(
+fun FlickerTestParameter.entireScreenCovered(
beginRotation: Int,
endRotation: Int = beginRotation,
allStates: Boolean = true
@@ -111,37 +64,21 @@ fun FlickerTestParameter.noUncoveredRegions(
}
}
-@JvmOverloads
-fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
- if (rotatesScreen) {
- assertLayers {
- this.isVisible(NAV_BAR_LAYER_NAME)
- .then()
- .isInvisible(NAV_BAR_LAYER_NAME)
- .then()
- .isVisible(NAV_BAR_LAYER_NAME)
- }
- } else {
- assertLayers {
- this.isVisible(NAV_BAR_LAYER_NAME)
- }
+fun FlickerTestParameter.navBarLayerIsVisible() {
+ assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ assertLayersEnd {
+ this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
-@JvmOverloads
-fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
- if (rotatesScreen) {
- assertLayers {
- this.isVisible(STATUS_BAR_LAYER_NAME)
- .then()
- .isInvisible(STATUS_BAR_LAYER_NAME)
- .then()
- .isVisible(STATUS_BAR_LAYER_NAME)
- }
- } else {
- assertLayers {
- this.isVisible(STATUS_BAR_LAYER_NAME)
- }
+fun FlickerTestParameter.statusBarLayerIsVisible() {
+ assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ assertLayersEnd {
+ this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
}
}
@@ -154,10 +91,10 @@ fun FlickerTestParameter.navBarLayerRotatesAndScales(
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
+ this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
+ this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
}
}
@@ -170,54 +107,46 @@ fun FlickerTestParameter.statusBarLayerRotatesScales(
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(startingPos)
+ this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(endingPos)
- }
-}
-
-fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) {
- assertLayers {
- this.isVisible(*HOME_WINDOW_TITLE)
- .then()
- .isVisible(appName)
+ this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
}
}
-fun FlickerTestParameter.launcherLayerReplacesApp(testApp: IAppHelper) {
+/**
+ * Asserts that:
+ * [originalLayer] is visible at the start of the trace
+ * [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
+ * becomes visible
+ * [newLayer] remains visible until the end of the trace
+ *
+ * @param originalLayer Layer that should be visible at the start
+ * @param newLayer Layer that should be visible at the end
+ * @param ignoreSnapshot If the snapshot layer should be ignored during the transition
+ * (useful mostly for app launch)
+ */
+fun FlickerTestParameter.replacesLayer(
+ originalLayer: ComponentName,
+ newLayer: ComponentName,
+ ignoreSnapshot: Boolean = false
+) {
assertLayers {
- this.isVisible(testApp.getPackage())
- .then()
- .isInvisible(testApp.getPackage())
- .isVisible(*HOME_WINDOW_TITLE)
+ val assertion = this.isVisible(originalLayer)
+ if (ignoreSnapshot) {
+ assertion.then()
+ .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ }
+ assertion.then().isVisible(newLayer)
}
-}
-fun FlickerTestParameter.layerBecomesVisible(packageName: String) {
- assertLayers {
- this.isInvisible(packageName)
- .then()
- .isVisible(packageName)
- }
-}
-
-fun FlickerTestParameter.layerBecomesInvisible(packageName: String) {
- assertLayers {
- this.isVisible(packageName)
- .then()
- .isInvisible(packageName)
+ assertLayersStart {
+ this.isVisible(originalLayer)
+ .isInvisible(newLayer)
}
-}
-fun FlickerTestParameter.focusChanges(vararg windows: String) {
- assertEventLog {
- this.focusChanges(windows)
+ assertLayersEnd {
+ this.isInvisible(originalLayer)
+ .isVisible(newLayer)
}
}
-
-fun FlickerTestParameter.focusDoesNotChange() {
- assertEventLog {
- this.focusDoesNotChange()
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 71184c2e0aa2..90c851d6e266 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2020 The Android Open Source Project
*
@@ -16,6 +17,8 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,6 +50,13 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @Postsubmit
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 6786279ae107..e8391ed9cfa1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,6 +49,13 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @Postsubmit
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index f7f977d7bd0a..f9e6babee938 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,30 +19,35 @@ package com.android.server.wm.flicker.close
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherLayerReplacesApp
-import com.android.server.wm.flicker.launcherWindowBecomesVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.replacesLayer
import org.junit.Test
+/**
+ * Base test class for transitions that close an app back to the launcher screen
+ */
abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ /**
+ * Specification of the test transition to execute
+ */
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
setup {
eachRun {
@@ -66,29 +71,29 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -118,25 +123,33 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun noUncoveredRegions() {
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun entireScreenCovered() {
+ testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
}
@Presubmit
@Test
open fun launcherReplacesAppWindowAsTopWindow() {
- testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ .then()
+ .isAppWindowOnTop(LAUNCHER_COMPONENT)
+ }
}
@Presubmit
@Test
open fun launcherWindowBecomesVisible() {
- testSpec.launcherWindowBecomesVisible()
+ testSpec.assertWm {
+ this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowOnTop(LAUNCHER_COMPONENT)
+ }
}
@Presubmit
@Test
open fun launcherLayerReplacesApp() {
- testSpec.launcherLayerReplacesApp(testApp)
+ testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 83fddae5b1a7..d224af97462e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -61,7 +61,8 @@ open class ImeAppHelper @JvmOverloads constructor(
if (wmHelper == null) {
device.waitForIdle()
} else {
- wmHelper.waitImeWindowShown()
+ wmHelper.waitImeShown()
+ wmHelper.waitForAppTransitionIdle()
}
}
@@ -78,7 +79,7 @@ open class ImeAppHelper @JvmOverloads constructor(
if (wmHelper == null) {
device.waitForIdle()
} else {
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
}
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index b5757fd21ee0..384d8e8e998d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -28,15 +28,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -46,6 +46,14 @@ import org.junit.runners.Parameterized
/**
* Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ * Don't show if this is not explicitly requested by the user and the input method
+ * is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
*/
@RequiresDevice
@@ -79,37 +87,55 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
}
@Presubmit
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+ fun imeAppWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+ @Presubmit
+ @Test
+ fun imeLayerVisibleStart() {
+ testSpec.assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerInvisibleEnd() {
+ testSpec.assertLayersEnd {
+ this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -117,15 +143,19 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@Presubmit
@Test
- fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+ fun imeAppLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
}
- @FlakyTest
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
@@ -145,8 +175,11 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(repetitions = 5,
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 549e44c511b9..ade215b3022d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -30,14 +30,14 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -47,6 +47,14 @@ import org.junit.runners.Parameterized
/**
* Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ * Don't show if this is not explicitly requested by the user and the input method
+ * is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
*/
@RequiresDevice
@@ -75,51 +83,73 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
}
}
}
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
}
- @FlakyTest
+ @FlakyTest(bugId = 190189685)
@Test
- fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
+ fun imeAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ .then()
+ .appWindowNotOnTop(testApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
- fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
+ Surface.ROTATION_0)
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
+ fun imeLayerVisibleStart() {
+ testSpec.assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
- @FlakyTest
+ @Presubmit
+ @Test
+ fun imeLayerInvisibleEnd() {
+ testSpec.assertLayersEnd {
+ this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
+
+ @Presubmit
@Test
fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
@Presubmit
@Test
- fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+ fun imeAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -133,18 +163,19 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
}
}
@@ -154,8 +185,11 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(repetitions = 1,
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 82ca074b5ef2..cdfcff3d4beb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -28,13 +28,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import org.junit.Assume
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
@@ -61,7 +61,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
return FlickerBuilder(instrumentation).apply {
setup {
test {
- testApp.launchViaIntent()
+ testApp.launchViaIntent(wmHelper)
}
eachRun {
testApp.openIME(device, wmHelper)
@@ -80,37 +80,42 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT))
}
}
@Presubmit
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+ fun imeAppWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
@Presubmit
@Test
@@ -146,7 +151,11 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+ fun imeAppLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 703e4a125440..05fc2672168e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -30,13 +30,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -68,7 +68,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
}
teardown {
eachRun {
@@ -84,19 +84,20 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT))
}
}
@@ -106,19 +107,25 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@FlakyTest
@Test
- fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+ fun imeAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp.component)
+ .then()
+ .isAppWindowInvisible(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
Surface.ROTATION_0)
@Presubmit
@@ -127,7 +134,13 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+ fun imeAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ }
+ }
@Presubmit
@Test
@@ -144,8 +157,9 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 7e34469b8188..7659d9471e2f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -14,128 +14,56 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker.ime
-import android.platform.helpers.IAppHelper
import com.android.server.wm.flicker.FlickerTestParameter
-
-const val IME_WINDOW_TITLE = "InputMethod"
-
-fun FlickerTestParameter.imeLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
- if (rotatesScreen) {
- assertLayers {
- this.isVisible(IME_WINDOW_TITLE)
- .then()
- .isInvisible(IME_WINDOW_TITLE)
- .then()
- .isVisible(IME_WINDOW_TITLE)
- }
- } else {
- assertLayers {
- this.isVisible(IME_WINDOW_TITLE)
- }
- }
-}
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
fun FlickerTestParameter.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(IME_WINDOW_TITLE)
+ this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .isVisible(IME_WINDOW_TITLE)
+ .isVisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
fun FlickerTestParameter.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(IME_WINDOW_TITLE)
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .isInvisible(IME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) {
- assertLayers {
- this.isVisible(testApp.getPackage())
- }
-}
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(testApp.getPackage())
+ .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.showsNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .showsNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
}
} else {
assertWm {
- this.showsNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
}
fun FlickerTestParameter.imeWindowBecomesVisible() {
assertWm {
- this.hidesNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .showsNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
fun FlickerTestParameter.imeWindowBecomesInvisible() {
assertWm {
- this.showsNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(
- testApp: IAppHelper,
- rotatesScreen: Boolean = false
-) {
- if (rotatesScreen) {
- assertWm {
- this.showsAppWindow(testApp.getPackage())
- .then()
- .hidesAppWindow(testApp.getPackage())
- .then()
- .showsAppWindow(testApp.getPackage())
- }
- } else {
- assertWm {
- this.showsAppWindow(testApp.getPackage())
- }
- }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) {
- assertWm {
- this.hidesAppWindow(windowName)
- .then()
- .showsAppWindow(windowName)
- }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .appWindowNotOnTop(testApp.getPackage())
- }
-}
-
-fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) {
- assertLayers {
- this.isVisible(testApp.getPackage())
- .then()
- .isInvisible(testApp.getPackage())
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index cae1b16c1c8c..f35a180e1ad6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -28,16 +28,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,11 +80,11 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
@@ -93,19 +92,23 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+ fun appWindowAlwaysVisibleOnTop() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
@Presubmit
@Test
@@ -115,7 +118,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Test
fun layerAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(testApp.`package`)
+ this.isVisible(testApp.component)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index b7673d5b0107..3bcf793e9071 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -26,23 +27,22 @@ import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,7 +61,6 @@ import org.junit.runners.Parameterized
class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
- private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -73,14 +72,14 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
eachRun {
device.pressRecentApps()
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
wmHelper.waitForAppTransitionIdle()
this.setRotation(testSpec.config.startRotation)
}
}
transitions {
device.reopenAppFromOverview(wmHelper)
- wmHelper.waitImeWindowShown()
+ wmHelper.waitImeShown()
}
teardown {
test {
@@ -92,23 +91,34 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ val component = ComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ component)
+ )
}
}
@Presubmit
@Test
- fun launcherWindowBecomesInvisible() = testSpec.launcherWindowBecomesInvisible()
+ fun launcherWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -116,30 +126,57 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp, true)
+ fun imeAppWindowVisibility() {
+ // the app starts visible in live tile, then becomes invisible during animation and
+ // is again launched. Since we log 1x per frame, sometimes the activity visibility and
+ // the app visibility are updated together, sometimes not, thus ignore activity check
+ // at the start
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .then()
+ .isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ .then()
+ .isAppWindowVisible(testApp.component)
+ }
+ }
@Presubmit
@Test
// During testing the launcher is always in portrait mode
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation)
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
- fun imeLayerIsAlwaysVisible() = testSpec.imeLayerIsAlwaysVisible(true)
+ fun imeLayerIsBecomesVisible() {
+ testSpec.assertLayers {
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .then()
+ .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .then()
+ .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
@Presubmit
@Test
- fun appLayerReplacesLauncher() =
- testSpec.appLayerReplacesLauncher(testAppComponentName.className)
+ fun appLayerReplacesLauncher() {
+ testSpec.assertLayers {
+ this.isVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(testApp.component)
+ }
+ }
@Presubmit
@Test
@@ -156,8 +193,14 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // depends on how much of the animation transactions are sent to SF at once
+ // sometimes this layer appears for 2-3 frames, sometimes for only 1
+ val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+ )
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 0cae37c8d5ab..f9dd88e8cb29 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,27 +17,26 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -54,10 +53,11 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group2
+@Presubmit
class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
- private val imeTestApp = ImeAppHelper(instrumentation)
+ private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -66,7 +66,13 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
eachRun {
this.setRotation(testSpec.config.startRotation)
testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+
imeTestApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+
imeTestApp.openIME(device, wmHelper)
}
}
@@ -74,57 +80,86 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
eachRun {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- }
- test {
- imeTestApp.exit(wmHelper)
+ testApp.exit()
+ imeTestApp.exit()
}
}
transitions {
// [Step1]: Swipe right from imeTestApp to testApp task
+ createTag(TAG_IME_VISIBLE)
val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- val displayCenterX = displayBounds.bounds.width() / 2
- device.swipe(displayCenterX, displayBounds.bounds.height(),
- displayBounds.bounds.width(), displayBounds.bounds.height(), 20)
+ device.swipe(0, displayBounds.bounds.height(),
+ displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+
wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+ createTag(TAG_IME_INVISIBLE)
}
transitions {
// [Step2]: Swipe left to back to imeTestApp task
val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- val displayCenterX = displayBounds.bounds.width() / 2
device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
- displayCenterX, displayBounds.bounds.height(), 20)
+ 0, displayBounds.bounds.height(), 50)
wmHelper.waitForFullScreenApp(imeTestApp.component)
}
}
}
- @FlakyTest
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(imeTestApp)
+ fun imeAppWindowVisibility() {
+ val component = ComponentName(imeTestApp.`package`, "")
+ testSpec.assertWm {
+ this.isAppWindowOnTop(component)
+ .then()
+ .isAppWindowVisible(component, ignoreActivity = true)
+ }
+ }
- @FlakyTest
@Test
- fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+ fun navBarLayerIsVisibleAroundSwitching() {
+ testSpec.assertLayersStart {
+ isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ }
- @FlakyTest
@Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+ fun statusBarLayerIsVisibleAroundSwitching() {
+ testSpec.assertLayersStart {
+ isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ }
- @Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun imeLayerIsVisibleWhenSwitchingToImeApp() {
+ testSpec.assertLayersStart {
+ isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ testSpec.assertLayersTag(TAG_IME_VISIBLE) {
+ isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
- @FlakyTest
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
+ testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
+ isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
- @Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
- @FlakyTest
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
companion object {
@Parameterized.Parameters(name = "{0}")
@@ -134,10 +169,13 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
.getConfigNonRotationTests(
repetitions = 3,
supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
+
+ private const val TAG_IME_VISIBLE = "imeVisible"
+ private const val TAG_IME_INVISIBLE = "imeInVisible"
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
deleted file mode 100644
index 01e34d9f8f97..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
-
-fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(*HOME_WINDOW_TITLE)
- .then()
- .showsAppWindowOnTop("Snapshot", testApp.getPackage())
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 9ff0bdfe66ba..e6dc8523acbf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -64,35 +64,17 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
@FlakyTest
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
-
- @FlakyTest
- @Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
- @FlakyTest
- @Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
+ @FlakyTest(bugId = 192721431)
@Test
override fun appLayerReplacesLauncher() {
super.appLayerReplacesLauncher()
}
- @FlakyTest
+ @FlakyTest(bugId = 192721431)
@Test
override fun appWindowReplacesLauncherAsTopWindow() {
super.appWindowReplacesLauncherAsTopWindow()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index b073a7ca1495..7833e2f25eec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -65,34 +65,10 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
@FlakyTest
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
- @FlakyTest
- @Test
- override fun statusBarLayerRotatesScales() {
- super.statusBarLayerRotatesScales()
- }
-
- @FlakyTest
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index b304d5f999df..860a5aed13fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -22,24 +22,24 @@ import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.replacesLayer
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
import org.junit.Test
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@@ -71,14 +71,14 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
@Presubmit
@@ -89,14 +89,14 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
}
@Presubmit
@@ -124,31 +124,43 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
// During testing the launcher is always in portrait mode
- open fun noUncoveredRegions() {
- testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+ open fun entireScreenCovered() {
+ testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation)
}
@Presubmit
@Test
open fun focusChanges() {
- testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+ testSpec.assertEventLog {
+ this.focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
@Presubmit
@Test
open fun appLayerReplacesLauncher() {
- testSpec.appLayerReplacesLauncher(testApp.`package`)
+ testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component)
}
@Presubmit
@Test
open fun appWindowReplacesLauncherAsTopWindow() {
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+ testSpec.assertWm {
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(testApp.component)
+ }
}
@Presubmit
@Test
open fun launcherWindowBecomesInvisible() {
- testSpec.launcherWindowBecomesInvisible()
+ testSpec.assertWm {
+ this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ }
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index e2705c764917..b509c61d2aba 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -67,15 +67,7 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
@FlakyTest
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- override fun navBarLayerRotatesAndScales() {
- super.navBarLayerRotatesAndScales()
- }
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 69e8a8d08e58..73986b6def8f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,7 +25,13 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,39 +67,62 @@ class ChangeAppRotationTest(
super.focusDoesNotChange()
}
- @Postsubmit
+ @Presubmit
@Test
fun screenshotLayerBecomesInvisible() {
testSpec.assertLayers {
- this.isVisible(testApp.getPackage())
+ this.isVisible(testApp.component)
.then()
- .isVisible(SCREENSHOT_LAYER)
+ .isVisible(ROTATION_COMPONENT)
.then()
- .isVisible(testApp.getPackage())
+ .isVisible(testApp.component)
}
}
+ @Presubmit
+ @Test
+ fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
+ }
+
@Postsubmit
@Test
- override fun statusBarLayerRotatesScales() {
- super.statusBarLayerRotatesScales()
+ fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
}
@Presubmit
@Test
- override fun navBarWindowIsAlwaysVisible() {
- super.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsVisible() {
+ super.navBarWindowIsVisible()
+ }
+
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisible() {
+ super.navBarLayerIsVisible()
}
@FlakyTest
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
+ override fun navBarLayerRotatesAndScales() {
+ super.navBarLayerRotatesAndScales()
}
- companion object {
- private const val SCREENSHOT_LAYER = "RotationLayer"
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 4b888cd5aad0..2b0b3c23a0b2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,25 +17,21 @@
package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Test
@@ -69,19 +65,19 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
}
}
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = true)
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(
@@ -90,31 +86,12 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true)
- }
-
- @FlakyTest
- @Test
- open fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
- }
-
- @FlakyTest
- @Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- "SecondaryHomeHandle"
+ ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ ComponentName("", "SecondaryHomeHandle")
)
)
}
@@ -130,22 +107,24 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun noUncoveredRegions() {
- testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ open fun entireScreenCovered() {
+ testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
}
@Presubmit
@Test
open fun focusDoesNotChange() {
- testSpec.focusDoesNotChange()
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
}
@Presubmit
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
- this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
+ this.visibleRegion(testApp.component).coversExactly(startingPos)
}
}
@@ -153,7 +132,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
- this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
+ this.visibleRegion(testApp.component).coversExactly(endingPos)
}
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index b153bece1133..b97b97768362 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.view.WindowManager
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +28,7 @@ import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,42 +62,91 @@ class SeamlessAppRotationTest(
}
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() {
- super.statusBarWindowIsAlwaysVisible()
+ fun appWindowFullScreen() {
+ testSpec.assertWm {
+ this.invoke("isFullScreen") {
+ val appWindow = it.windowState(testApp.`package`)
+ val flags = appWindow.windowState?.attributes?.flags ?: 0
+ appWindow.verify("isFullScreen")
+ .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
+ .isGreaterThan(0)
+ }
+ }
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
+ fun appWindowSeamlessRotation() {
+ testSpec.assertWm {
+ this.invoke("isRotationSeamless") {
+ val appWindow = it.windowState(testApp.`package`)
+ val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
+ appWindow.verify("isRotationSeamless")
+ .that(rotationAnimation
+ .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS))
+ .isGreaterThan(0)
+ }
+ }
}
@Presubmit
@Test
fun appLayerAlwaysVisible() {
testSpec.assertLayers {
- isVisible(testApp.`package`)
+ isVisible(testApp.component)
}
}
- @FlakyTest(bugId = 185400889)
+ @Postsubmit
@Test
fun appLayerRotates() {
testSpec.assertLayers {
- this.coversExactly(startingPos, testApp.`package`)
+ this.coversExactly(startingPos, testApp.component)
.then()
- .coversExactly(endingPos, testApp.`package`)
+ .coversExactly(endingPos, testApp.component)
}
}
- @Postsubmit
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysInvisible() {
+ testSpec.assertWm {
+ this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysInvisible() {
+ testSpec.assertLayers {
+ this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ }
+
+ @Presubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsVisible() {
+ super.navBarWindowIsVisible()
+ }
+
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisible() {
+ super.navBarLayerIsVisible()
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
companion object {
private val testFactory = FlickerTestParameterFactory.getInstance()
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 4708cfd48381..c55e7c2720db 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -23,5 +23,6 @@
<EditText android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
+ android:imeOptions="flagNoExtractUi"
android:inputType="text"/>
</LinearLayout>
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 2c6c8d7cae20..1381c0a43c30 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -73,6 +73,14 @@
android:text="@string/play_trigger"
android:onClick="onPlayTriggerButtonClicked"
android:padding="20dp" />
+
+ <Button
+ android:id="@+id/get_state_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/get_model_state"
+ android:onClick="onGetModelStateButtonClicked"
+ android:padding="20dp" />
</LinearLayout>
<LinearLayout
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index c48b64884c5e..adb0fcf8e185 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -22,6 +22,7 @@
<string name="start_recog">Start</string>
<string name="stop_recog">Stop</string>
<string name="play_trigger">Play Trigger Audio</string>
+ <string name="get_model_state">Get State</string>
<string name="capture">Capture Audio</string>
<string name="stop_capture">Stop Capturing Audio</string>
<string name="play_capture">Play Captured Audio</string>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
index c3c4cf556986..72aa38dc7e4b 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -257,6 +257,14 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
}
}
+ public synchronized void onGetModelStateButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't get model state: not bound to SoundTriggerTestService");
+ } else {
+ mService.getModelState(mSelectedModelUuid);
+ }
+ }
+
public synchronized void onCaptureAudioCheckboxClicked(View v) {
// See if we have the right permissions
if (!mService.hasMicrophonePermission()) {
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 380e29984c63..6d4ffcff7d45 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -46,6 +48,7 @@ import java.util.Properties;
import java.util.Random;
import java.util.UUID;
+
public class SoundTriggerTestService extends Service {
private static final String TAG = "SoundTriggerTestSrv";
private static final String INTENT_ACTION = "com.android.intent.action.MANAGE_SOUND_TRIGGER";
@@ -57,6 +60,8 @@ public class SoundTriggerTestService extends Service {
private Random mRandom;
private UserActivity mUserActivity;
+ private static int captureCount;
+
public interface UserActivity {
void addModel(UUID modelUuid, String state);
void setModelState(UUID modelUuid, String state);
@@ -131,6 +136,8 @@ public class SoundTriggerTestService extends Service {
} else if (command.equals("set_capture_timeout")) {
setCaptureAudioTimeout(getModelUuidFromIntent(intent),
intent.getIntExtra("timeout", 5000));
+ } else if (command.equals("get_model_state")) {
+ getModelState(getModelUuidFromIntent(intent));
} else {
Log.e(TAG, "Unknown command '" + command + "'");
}
@@ -432,6 +439,17 @@ public class SoundTriggerTestService extends Service {
return modelInfo != null && modelInfo.captureAudioTrack != null;
}
+ public synchronized void getModelState(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ int status = mSoundTriggerUtil.getModelState(modelUuid);
+ postMessage("GetModelState for: " + modelInfo.name + " returns: "
+ + status);
+ }
+
private void loadModelsInDataDir() {
// Load all the models in the data dir.
boolean loadedModel = false;
@@ -527,18 +545,29 @@ public class SoundTriggerTestService extends Service {
}
}
+
private class CaptureAudioRecorder implements Runnable {
private final ModelInfo mModelInfo;
+
+ // EventPayload and RecognitionEvent are equivalant. Only one will be non-null.
private final SoundTriggerDetector.EventPayload mEvent;
+ private final RecognitionEvent mRecognitionEvent;
public CaptureAudioRecorder(ModelInfo modelInfo, SoundTriggerDetector.EventPayload event) {
mModelInfo = modelInfo;
mEvent = event;
+ mRecognitionEvent = null;
+ }
+
+ public CaptureAudioRecorder(ModelInfo modelInfo, RecognitionEvent event) {
+ mModelInfo = modelInfo;
+ mEvent = null;
+ mRecognitionEvent = event;
}
@Override
public void run() {
- AudioFormat format = mEvent.getCaptureAudioFormat();
+ AudioFormat format = getAudioFormat();
if (format == null) {
postErrorToast("No audio format in recognition event.");
return;
@@ -600,18 +629,21 @@ public class SoundTriggerTestService extends Service {
}
audioRecord = new AudioRecord(attributes, format, bytesRequired,
- mEvent.getCaptureSession());
+ getCaptureSession());
byte[] buffer = new byte[bytesRequired];
// Create a file so we can save the output data there for analysis later.
FileOutputStream fos = null;
try {
- fos = new FileOutputStream( new File(
- getFilesDir() + File.separator
- + mModelInfo.name.replace(' ', '_')
- + "_capture_" + format.getChannelCount() + "ch_"
- + format.getSampleRate() + "hz_" + encoding + ".pcm"));
+ File file = new File(
+ getFilesDir() + File.separator
+ + mModelInfo.name.replace(' ', '_')
+ + "_capture_" + format.getChannelCount() + "ch_"
+ + format.getSampleRate() + "hz_" + encoding
+ + "_" + (++captureCount) + ".pcm");
+ Log.i(TAG, "Writing audio to: " + file);
+ fos = new FileOutputStream(file);
} catch (IOException e) {
Log.e(TAG, "Failed to open output for saving PCM data", e);
postErrorToast("Failed to open output for saving PCM data: "
@@ -635,6 +667,10 @@ public class SoundTriggerTestService extends Service {
bytesRequired -= bytesRead;
}
audioRecord.stop();
+ if (fos != null) {
+ fos.flush();
+ fos.close();
+ }
} catch (Exception e) {
Log.e(TAG, "Error recording trigger audio", e);
postErrorToast("Error recording trigger audio: " + e.getMessage());
@@ -651,6 +687,26 @@ public class SoundTriggerTestService extends Service {
setModelState(mModelInfo, "Recording finished");
}
}
+
+ private AudioFormat getAudioFormat() {
+ if (mEvent != null) {
+ return mEvent.getCaptureAudioFormat();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureFormat;
+ }
+ return null;
+ }
+
+ private int getCaptureSession() {
+ if (mEvent != null) {
+ return mEvent.getCaptureSession();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureSession;
+ }
+ return 0;
+ }
}
// Implementation of SoundTriggerDetector.Callback.
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index cfe8c855ac81..996a78f2e42a 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,6 +18,8 @@ package com.android.test.soundtrigger;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.RemoteException;
@@ -27,6 +29,7 @@ import android.util.Log;
import com.android.internal.app.ISoundTriggerService;
+import java.lang.reflect.Method;
import java.lang.RuntimeException;
import java.util.UUID;
@@ -50,13 +53,31 @@ public class SoundTriggerUtil {
* The sound model must contain a valid UUID.
*
* @param soundModel The sound model to add/update.
+ * @return The true if the model was loaded successfully, false otherwise.
*/
public boolean addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
if (soundModel == null) {
throw new RuntimeException("Bad sound model");
}
mSoundTriggerManager.updateModel(soundModel);
- return true;
+ // TODO: call loadSoundModel in the soundtrigger manager updateModel method
+ // instead of here. It is needed to keep soundtrigger manager internal
+ // state consistent.
+ return mSoundTriggerManager
+ .loadSoundModel(getGenericSoundModel(soundModel)) == 0;
+ }
+
+ private GenericSoundModel getGenericSoundModel(
+ SoundTriggerManager.Model soundModel) {
+ try {
+ Method method = SoundTriggerManager.Model.class
+ .getDeclaredMethod("getGenericSoundModel");
+ method.setAccessible(true);
+ return (GenericSoundModel) method.invoke(soundModel);
+ } catch (ReflectiveOperationException e) {
+ Log.e(TAG, "Failed to getGenericSoundModel: " + soundModel, e);
+ return null;
+ }
}
/**
@@ -92,6 +113,16 @@ public class SoundTriggerUtil {
return true;
}
+ /**
+ * Get the current model state
+ *
+ * @param modelId The model ID to look-up the sound model for.
+ * @return 0 if the call succeeds, or an error code if it fails.
+ */
+ public int getModelState(UUID modelId) {
+ return mSoundTriggerManager.getModelState(modelId);
+ }
+
public SoundTriggerDetector createSoundTriggerDetector(UUID modelId,
SoundTriggerDetector.Callback callback) {
return mSoundTriggerManager.createSoundTriggerDetector(modelId, callback, null);
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index e07fbbf7a1c1..dc090aa2d686 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,15 +37,19 @@ android_test {
"vts",
],
data: [
- ":NotoColorEmojiTtf",
+ ":NotoColorEmoji.ttf",
+ ":NotoSerif-Regular.ttf",
+ ":NotoSerif-Bold.ttf",
":UpdatableSystemFontTestCertDer",
- ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ ":UpdatableSystemFontTest_NotoColorEmoji.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
],
sdk_version: "test_current",
}
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 4f6487e7e953..6effa7bd0a50 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -29,13 +29,17 @@
<option name="cleanup" value="true" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
<option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig" />
+ <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
+ <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
index 947e9c2ff56a..a8c27fb0f116 100644
--- a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
+++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
@@ -20,6 +20,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.Activity;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -27,14 +28,20 @@ import android.widget.TextView;
/** Test app to render an emoji. */
public class EmojiRenderingTestActivity extends Activity {
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
- TextView textView = new TextView(this);
- textView.setText("\uD83E\uDD72"); // 🥲
- container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView emojiTextView = new TextView(this);
+ emojiTextView.setText("\uD83E\uDD72"); // 🥲
+ container.addView(emojiTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView serifTextView = new TextView(this);
+ serifTextView.setTypeface(Typeface.create(TEST_NOTO_SERIF, Typeface.NORMAL));
+ serifTextView.setText(TEST_NOTO_SERIF);
+ container.addView(serifTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(container);
}
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6bd07d0a84fd..87fda0d220e5 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -16,6 +16,9 @@
package com.android.updatablesystemfont;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_BOLD;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static com.google.common.truth.Truth.assertThat;
@@ -30,6 +33,7 @@ import android.content.Context;
import android.graphics.fonts.FontFamilyUpdateRequest;
import android.graphics.fonts.FontFileUpdateRequest;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontStyle;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.RootPermissionTest;
import android.security.FileIntegrityManager;
@@ -77,31 +81,45 @@ public class UpdatableSystemFontTest {
private static final String SYSTEM_FONTS_DIR = "/system/fonts/";
private static final String DATA_FONTS_DIR = "/data/fonts/files/";
private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
- private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
+ private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
+ private static final String NOTO_COLOR_EMOJI_TTF =
"/data/local/tmp/NotoColorEmoji.ttf";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+ private static final String NOTO_COLOR_EMOJI_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
// A font with revision == 0.
private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V0_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig";
// A font with revision == original + 1
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig";
// A font with revision == original + 2
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig";
+
+ private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
+ private static final String NOTO_SERIF_REGULAR_TTF =
+ "/data/local/tmp/NotoSerif-Regular.ttf";
+ private static final String NOTO_SERIF_REGULAR_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
+
+ private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
+ private static final String NOTO_SERIF_BOLD_TTF =
+ "/data/local/tmp/NotoSerif-Bold.ttf";
+ private static final String NOTO_SERIF_BOLD_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp";
private static final String EMOJI_RENDERING_TEST_ACTIVITY =
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
+ // This should be the same as the one in EmojiRenderingTestActivity.
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
private static final String GET_AVAILABLE_FONTS_TEST_ACTIVITY =
@@ -141,11 +159,20 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont() throws Exception {
+ FontConfig oldFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
+ // Check that font config is updated.
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
+ FontConfig newFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
+ assertThat(newFontConfig.getConfigVersion())
+ .isGreaterThan(oldFontConfig.getConfigVersion());
+ assertThat(newFontConfig.getLastModifiedTimeMillis())
+ .isGreaterThan(oldFontConfig.getLastModifiedTimeMillis());
// The updated font should be readable and unmodifiable.
expectCommandToSucceed("dd status=none if=" + fontPath + " of=/dev/null");
expectCommandToFail("dd status=none if=" + CERT_PATH + " of=" + fontPath);
@@ -154,11 +181,11 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont_twice() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
@@ -173,16 +200,16 @@ public class UpdatableSystemFontTest {
public void updateFont_allowSameVersion() throws Exception {
// Update original font to the same version
assertThat(updateFontFile(
- ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG))
+ NOTO_COLOR_EMOJI_TTF, NOTO_COLOR_EMOJI_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
// Update updated font to the same version
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -195,28 +222,58 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont_invalidCert() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE);
}
@Test
public void updateFont_downgradeFromSystem() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
public void updateFont_downgradeFromData() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
+ public void updateFontFamily() throws Exception {
+ assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(0).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(0).getStyle().getWeight())
+ .isEqualTo(FONT_WEIGHT_NORMAL);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(1).getStyle().getWeight()).isEqualTo(FONT_WEIGHT_BOLD);
+ }
+
+ @Test
+ public void updateFontFamily_asNewFont() throws Exception {
+ assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
+ .isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ }
+
+ @Test
public void launchApp() throws Exception {
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR);
@@ -231,22 +288,25 @@ public class UpdatableSystemFontTest {
String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR);
+ updateNotoSerifAs(TEST_NOTO_SERIF);
+ String notoSerifPath = getFontPath(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
startActivity(EMOJI_RENDERING_TEST_APP_ID, EMOJI_RENDERING_TEST_ACTIVITY);
// The original font should NOT be opened by the app.
SystemUtil.eventually(() -> {
assertThat(isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
assertThat(isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse();
+ assertThat(isFileOpenedBy(notoSerifPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
}, ACTIVITY_TIMEOUT_MILLIS);
}
@Test
public void reboot() throws Exception {
expectCommandToSucceed(String.format("cmd font update %s %s",
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -264,7 +324,7 @@ public class UpdatableSystemFontTest {
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
for (int i = 0; i < 10; i++) {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
List<String> openFiles = getOpenFiles("system_server");
for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
@@ -285,7 +345,7 @@ public class UpdatableSystemFontTest {
public void fdLeakTest_withoutPermission() throws Exception {
Pattern patternEmojiVPlus1 =
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
- byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(
new File(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF), MODE_READ_ONLY)) {
assertThrows(SecurityException.class,
@@ -340,18 +400,56 @@ public class UpdatableSystemFontTest {
configVersion);
}
+ private int updateNotoSerifAs(String familyName) throws IOException {
+ List<FontFamilyUpdateRequest.Font> fonts = Arrays.asList(
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT)).build(),
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_BOLD_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT)).build());
+ FontFamilyUpdateRequest.FontFamily fontFamily =
+ new FontFamilyUpdateRequest.FontFamily.Builder(familyName, fonts).build();
+ byte[] regularSig = Files.readAllBytes(Paths.get(NOTO_SERIF_REGULAR_SIG));
+ byte[] boldSig = Files.readAllBytes(Paths.get(NOTO_SERIF_BOLD_SIG));
+ try (ParcelFileDescriptor regularFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_REGULAR_TTF), MODE_READ_ONLY);
+ ParcelFileDescriptor boldFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_BOLD_TTF), MODE_READ_ONLY)) {
+ return SystemUtil.runWithShellPermissionIdentity(() -> {
+ FontConfig fontConfig = mFontManager.getFontConfig();
+ return mFontManager.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(regularFd, regularSig))
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(boldFd, boldSig))
+ .addFontFamily(fontFamily)
+ .build(), fontConfig.getConfigVersion());
+ });
+ }
+ }
+
private String getFontPath(String psName) {
- return SystemUtil.runWithShellPermissionIdentity(() -> {
- FontConfig fontConfig = mFontManager.getFontConfig();
- for (FontConfig.FontFamily family : fontConfig.getFontFamilies()) {
- for (FontConfig.Font font : family.getFontList()) {
- if (psName.equals(font.getPostScriptName())) {
- return font.getFile().getAbsolutePath();
- }
- }
- }
- throw new AssertionError("Font not found: " + psName);
- });
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .flatMap(family -> family.getFontList().stream())
+ .filter(font -> psName.equals(font.getPostScriptName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Font not found: " + psName))
+ .getFile()
+ .getAbsolutePath();
+ }
+
+ private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .filter(family -> familyName.equals(family.getName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Family not found: " + familyName));
}
private static void startActivity(String appId, String activityId) throws Exception {
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 426464ea574e..5eba3365ae5e 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -48,9 +48,9 @@ genrule_defaults {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -59,9 +59,9 @@ genrule {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -70,9 +70,9 @@ genrule {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -94,29 +94,43 @@ genrule_defaults {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmoji.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmoji.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiV0Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoSerif-Regular.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoSerif-Bold.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index bcd6ed73e133..824f91e1e826 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -45,6 +45,7 @@ public final class FrameworksTestsFilter extends SelectTest {
// Test specifications for FrameworksMockingCoreTests.
"android.app.activity.ActivityThreadClientTest",
"android.view.DisplayTest",
+ "android.window.ConfigurationHelperTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
"android.view.CutoutSpecificationTest",
@@ -59,10 +60,8 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.RoundedCornersTest",
"android.view.WindowMetricsTest",
"android.view.PendingInsetsControllerTest",
- "android.window.WindowContextTest",
- "android.window.WindowMetricsHelperTest",
+ "android.window.", // all tests under the package.
"android.app.activity.ActivityThreadTest",
- "android.window.WindowContextControllerTest"
};
public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 96f6512fe7f5..ebe41da5a03e 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@ namespace aapt {
static ApiVersion sDevelopmentSdkLevel = 10000;
static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
- "Q", "R", "S"
+ "Q", "R", "S", "T"
});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 81d35efaf29f..3cdb27c74d63 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -8,7 +8,21 @@ elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then
# Change is explicitly marked as ok to skip AOSP
exit 0
else
- # Change appears to be non-AOSP; search for files
+ # Change appears to be non-AOSP.
+
+ # If this is a cherry-pick, then allow it.
+ cherrypick=0
+ while read -r line ; do
+ if [[ $line =~ cherry\ picked\ from ]] ; then
+ (( cherrypick++ ))
+ fi
+ done < <(git show $1)
+ if (( cherrypick != 0 )); then
+ # This is a cherry-pick, so allow it.
+ exit 0
+ fi
+
+ # See if any files are affected.
count=0
while read -r file ; do
if (( count == 0 )); then
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa7847fcff..9ceb2042d74e 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@ inline infix fun Int.times(action: () -> Unit) {
* cccc dd
*/
fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
- val col1w = map { (a, _) -> a.length }.max()!!
- val col2w = map { (_, b) -> b.length }.max()!!
+ val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+ val col2w = map { (_, b) -> b.length }.maxOrNull()!!
return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
}
diff --git a/tools/lint/Android.bp b/tools/lint/Android.bp
new file mode 100644
index 000000000000..dcbc32b4e288
--- /dev/null
+++ b/tools/lint/Android.bp
@@ -0,0 +1,46 @@
+// 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 {
+ // 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"],
+}
+
+java_library_host {
+ name: "AndroidFrameworkLintChecker",
+ srcs: ["checks/src/main/java/**/*.kt"],
+ plugins: ["auto_service_plugin"],
+ libs: [
+ "auto_service_annotations",
+ "lint_api",
+ ],
+}
+
+// TODO: (b/162368644) Implement these (working in gradle) Kotlin Tests to run on Soong
+//java_test_host {
+// name: "AndroidFrameworkLintCheckerTest",
+// srcs: [
+// "checks/src/test/java/**/*.kt",
+// "checks/src/main/java/**/*.kt",
+// ],
+// plugins: ["auto_service_plugin"],
+// static_libs: [
+// "auto_service_annotations",
+// "lint_api",
+// ],
+//}
diff --git a/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt
new file mode 100644
index 000000000000..a41d65d7ea3a
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt
@@ -0,0 +1,360 @@
+/*
+ * 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.lint
+
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_NESTED_CLEAR_IDENTITY_CALLS
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_NON_FINAL_TOKEN
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_UNUSED_TOKEN
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageClearIdentityCallNotFollowedByTryFinally
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNestedClearIdentityCallsPrimary
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNestedClearIdentityCallsSecondary
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNonFinalToken
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageRestoreIdentityCallNotInFinallyBlock
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageUnusedToken
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.search.PsiSearchScopeUtil
+import com.intellij.psi.search.SearchScope
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UDeclarationsExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.ULocalVariable
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.UTryExpression
+import org.jetbrains.uast.getParentOfType
+
+/**
+ * Lint Detector that finds issues with improper usages of the token returned by
+ * Binder.clearCallingIdentity()
+ */
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
+ private companion object {
+ const val CLASS_BINDER = "android.os.Binder"
+ const val CLASS_USER_HANDLE = "android.os.UserHandle"
+
+ @JvmField
+ val callerAwareMethods = listOf(
+ Method.BINDER_GET_CALLING_PID,
+ Method.BINDER_GET_CALLING_UID,
+ Method.BINDER_GET_CALLING_UID_OR_THROW,
+ Method.BINDER_GET_CALLING_USER_HANDLE,
+ Method.USER_HANDLE_GET_CALLING_APP_ID,
+ Method.USER_HANDLE_GET_CALLING_USER_ID
+ )
+ }
+
+ /** Map of <Token variable name, Token object> */
+ private val tokensMap = mutableMapOf<String, Token>()
+
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(ULocalVariable::class.java, UQualifiedReferenceExpression::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ TokenUastHandler(context)
+
+ /** File analysis starts with a clear map */
+ override fun beforeCheckFile(context: Context) {
+ tokensMap.clear()
+ }
+
+ /**
+ * - If tokensMap has tokens after checking the file -> reports all locations as unused token
+ * issue incidents
+ * - File analysis ends with a clear map
+ */
+ override fun afterCheckFile(context: Context) {
+ for (token in tokensMap.values) {
+ context.report(
+ ISSUE_UNUSED_TOKEN,
+ token.location,
+ getIncidentMessageUnusedToken(token.variableName)
+ )
+ }
+ tokensMap.clear()
+ }
+
+ /** UAST handler that analyses elements and reports incidents */
+ private inner class TokenUastHandler(val context: JavaContext) : UElementHandler() {
+ /**
+ * For every variable initialization with Binder.clearCallingIdentity():
+ * - Checks for non-final token issue
+ * - Checks for unused token issue within different scopes
+ * - Checks for nested calls of clearCallingIdentity() issue
+ * - Checks for clearCallingIdentity() not followed by try-finally issue
+ * - Stores token variable name, scope in the file, location and finally block in tokensMap
+ */
+ override fun visitLocalVariable(node: ULocalVariable) {
+ val rhsExpression = node.uastInitializer as? UQualifiedReferenceExpression ?: return
+ if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
+ val location = context.getLocation(node as UElement)
+ val variableName = node.getName()
+ if (!node.isFinal) {
+ context.report(
+ ISSUE_NON_FINAL_TOKEN,
+ location,
+ getIncidentMessageNonFinalToken(variableName)
+ )
+ }
+ // If there exists an unused variable with the same name in the map, we can imply that
+ // we left the scope of the previous declaration, so we need to report the unused token
+ val oldToken = tokensMap[variableName]
+ if (oldToken != null) {
+ context.report(
+ ISSUE_UNUSED_TOKEN,
+ oldToken.location,
+ getIncidentMessageUnusedToken(oldToken.variableName)
+ )
+ }
+ // If there exists a token in the same scope as the current new token, it means that
+ // clearCallingIdentity() has been called at least twice without immediate restoration
+ // of identity, so we need to report the nested call of clearCallingIdentity()
+ val firstCallToken = findFirstTokenInScope(node)
+ if (firstCallToken != null) {
+ context.report(
+ ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ createNestedLocation(firstCallToken, location),
+ getIncidentMessageNestedClearIdentityCallsPrimary(
+ firstCallToken.variableName,
+ variableName
+ )
+ )
+ }
+ // If the next statement in the tree is not a try-finally statement, we need to report
+ // the "clearCallingIdentity() is not followed by try-finally" issue
+ val finallyClause = (getNextStatementOfLocalVariable(node) as? UTryExpression)
+ ?.finallyClause
+ if (finallyClause == null) {
+ context.report(
+ ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
+ location,
+ getIncidentMessageClearIdentityCallNotFollowedByTryFinally(variableName)
+ )
+ }
+ tokensMap[variableName] = Token(
+ variableName,
+ node.sourcePsi?.getUseScope(),
+ location,
+ finallyClause
+ )
+ }
+
+ /**
+ * For every class.method():
+ * - Checks use of caller-aware methods issue
+ * For every call of Binder.restoreCallingIdentity(token):
+ * - Checks for restoreCallingIdentity() not in the finally block issue
+ * - Removes token from tokensMap if token is within the scope of the method
+ */
+ override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
+ val token = findFirstTokenInScope(node)
+ if (isCallerAwareMethod(node) && token != null) {
+ context.report(
+ ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ context.getLocation(node),
+ getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+ token.variableName,
+ node.asRenderString()
+ )
+ )
+ return
+ }
+ if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
+ val selector = node.selector as UCallExpression
+ val arg = selector.valueArguments[0] as? USimpleNameReferenceExpression ?: return
+ val variableName = arg.identifier
+ val originalScope = tokensMap[variableName]?.scope ?: return
+ val psi = arg.sourcePsi ?: return
+ // Checks if Binder.restoreCallingIdentity(token) is called within the scope of the
+ // token declaration. If not within the scope, no action is needed because the token is
+ // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity()
+ if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return
+ // We do not report "restore identity call not in finally" issue when there is no
+ // finally block because that case is already handled by "clear identity call not
+ // followed by try-finally" issue
+ if (tokensMap[variableName]?.finallyBlock != null &&
+ node.uastParent != tokensMap[variableName]?.finallyBlock) {
+ context.report(
+ ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ context.getLocation(node),
+ getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName)
+ )
+ }
+ tokensMap.remove(variableName)
+ }
+
+ private fun isCallerAwareMethod(expression: UQualifiedReferenceExpression): Boolean =
+ callerAwareMethods.any { method -> isMethodCall(expression, method) }
+
+ private fun isMethodCall(
+ expression: UQualifiedReferenceExpression,
+ method: Method
+ ): Boolean {
+ val psiMethod = expression.resolve() as? PsiMethod ?: return false
+ return psiMethod.getName() == method.methodName &&
+ context.evaluator.methodMatches(
+ psiMethod,
+ method.className,
+ /* allowInherit */ true,
+ *method.args
+ )
+ }
+
+ /**
+ * ULocalVariable in the file tree:
+ *
+ * UBlockExpression
+ * UDeclarationsExpression
+ * ULocalVariable
+ * ULocalVariable
+ * UTryStatement
+ * etc.
+ *
+ * To get the next statement of ULocalVariable:
+ * - If there exists a next sibling in UDeclarationsExpression, return the sibling
+ * - If there exists a next sibling of UDeclarationsExpression in UBlockExpression, return
+ * the sibling
+ * - Otherwise, return null
+ *
+ * Example 1 - the next sibling is in UDeclarationsExpression:
+ * Code:
+ * {
+ * int num1 = 0, num2 = methodThatThrowsException();
+ * }
+ * Returns: num2 = methodThatThrowsException()
+ *
+ * Example 2 - the next sibling is in UBlockExpression:
+ * Code:
+ * {
+ * int num1 = 0;
+ * methodThatThrowsException();
+ * }
+ * Returns: methodThatThrowsException()
+ *
+ * Example 3 - no next sibling;
+ * Code:
+ * {
+ * int num1 = 0;
+ * }
+ * Returns: null
+ */
+ private fun getNextStatementOfLocalVariable(node: ULocalVariable): UElement? {
+ val declarationsExpression = node.uastParent as? UDeclarationsExpression ?: return null
+ val declarations = declarationsExpression.declarations
+ val indexInDeclarations = declarations.indexOf(node)
+ if (indexInDeclarations != -1 && declarations.size > indexInDeclarations + 1) {
+ return declarations[indexInDeclarations + 1]
+ }
+ val enclosingBlock = node
+ .getParentOfType<UBlockExpression>(strict = true) ?: return null
+ val expressions = enclosingBlock.expressions
+ val indexInBlock = expressions.indexOf(declarationsExpression as UElement)
+ return if (indexInBlock == -1) null else expressions.getOrNull(indexInBlock + 1)
+ }
+ }
+
+ private fun findFirstTokenInScope(node: UElement): Token? {
+ val psi = node.sourcePsi ?: return null
+ for (token in tokensMap.values) {
+ if (token.scope != null && PsiSearchScopeUtil.isInScope(token.scope, psi)) {
+ return token
+ }
+ }
+ return null
+ }
+
+ /**
+ * Creates a new instance of the primary location with the secondary location
+ *
+ * Here, secondary location is the helper location that shows where the issue originated
+ *
+ * The detector reports locations as objects, so when we add a secondary location to a location
+ * that has multiple issues, the secondary location gets displayed every time a location is
+ * referenced.
+ *
+ * Example:
+ * 1: final long token1 = Binder.clearCallingIdentity();
+ * 2: long token2 = Binder.clearCallingIdentity();
+ * 3: Binder.restoreCallingIdentity(token1);
+ * 4: Binder.restoreCallingIdentity(token2);
+ *
+ * Explanation:
+ * token2 has 2 issues: NonFinal and NestedCalls
+ *
+ * Lint report without cloning Lint report with cloning
+ * line 2: [NonFinalIssue] line 2: [NonFinalIssue]
+ * line 1: [NestedCallsIssue]
+ * line 2: [NestedCallsIssue] line 2: [NestedCallsIssue]
+ * line 1: [NestedCallsIssue] line 1: [NestedCallsIssue]
+ */
+ private fun createNestedLocation(
+ firstCallToken: Token,
+ secondCallTokenLocation: Location
+ ): Location {
+ return cloneLocation(secondCallTokenLocation)
+ .withSecondary(
+ cloneLocation(firstCallToken.location),
+ getIncidentMessageNestedClearIdentityCallsSecondary(
+ firstCallToken.variableName
+ )
+ )
+ }
+
+ private fun cloneLocation(location: Location): Location {
+ // smart cast of location.start to 'Position' is impossible, because 'location.start' is a
+ // public API property declared in different module
+ val locationStart = location.start
+ return if (locationStart == null) {
+ Location.create(location.file)
+ } else {
+ Location.create(location.file, locationStart, location.end)
+ }
+ }
+
+ private enum class Method(
+ val className: String,
+ val methodName: String,
+ val args: Array<String>
+ ) {
+ BINDER_CLEAR_CALLING_IDENTITY(CLASS_BINDER, "clearCallingIdentity", emptyArray()),
+ BINDER_RESTORE_CALLING_IDENTITY(CLASS_BINDER, "restoreCallingIdentity", arrayOf("long")),
+ BINDER_GET_CALLING_PID(CLASS_BINDER, "getCallingPid", emptyArray()),
+ BINDER_GET_CALLING_UID(CLASS_BINDER, "getCallingUid", emptyArray()),
+ BINDER_GET_CALLING_UID_OR_THROW(CLASS_BINDER, "getCallingUidOrThrow", emptyArray()),
+ BINDER_GET_CALLING_USER_HANDLE(CLASS_BINDER, "getCallingUserHandle", emptyArray()),
+ USER_HANDLE_GET_CALLING_APP_ID(CLASS_USER_HANDLE, "getCallingAppId", emptyArray()),
+ USER_HANDLE_GET_CALLING_USER_ID(CLASS_USER_HANDLE, "getCallingUserId", emptyArray())
+ }
+
+ private data class Token(
+ val variableName: String,
+ val scope: SearchScope?,
+ val location: Location,
+ val finallyBlock: UElement?
+ )
+}
diff --git a/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt
new file mode 100644
index 000000000000..1bd63ea17374
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt
@@ -0,0 +1,274 @@
+/*
+ * 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.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+// TODO: uncomment when lint API in Soong becomes 30.0+
+// import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenIssueRegistry : IssueRegistry() {
+ override val issues = listOf(
+ ISSUE_UNUSED_TOKEN,
+ ISSUE_NON_FINAL_TOKEN,
+ ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+ )
+
+ override val api: Int
+ get() = CURRENT_API
+
+ override val minApi: Int
+ get() = 8
+
+// TODO: uncomment when lint API in Soong becomes 30.0+
+// override val vendor: Vendor = Vendor(
+// vendorName = "Android Open Source Project",
+// feedbackUrl = "http://b/issues/new?component=315013",
+// contact = "brufino@google.com"
+// )
+
+ companion object {
+ /** Issue: unused token from Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_UNUSED_TOKEN: Issue = Issue.create(
+ id = "UnusedTokenOfOriginalCallingIdentity",
+ briefDescription = "Unused token of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but have not used the returned token to \
+ restore the identity.
+
+ Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \
+ of the method or when you need to restore the identity.
+
+ `token` is the result of `Binder.clearCallingIdentity()`
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has not been " +
+ "used to restore the calling identity. Introduce a `try`-`finally` after the " +
+ "declaration and call `Binder.restoreCallingIdentity($variableName)` in " +
+ "`finally` or remove `$variableName`."
+
+ /** Issue: non-final token from Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create(
+ id = "NonFinalTokenOfOriginalCallingIdentity",
+ briefDescription = "Non-final token of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but have not made the returned token `final`.
+
+ The token should be `final` in order to prevent it from being overwritten, \
+ which can cause problems when restoring the identity with \
+ `Binder.restoreCallingIdentity(token)`.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is a " +
+ "non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " +
+ "`$variableName`."
+
+ /** Issue: nested calls of Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create(
+ id = "NestedClearCallingIdentityCalls",
+ briefDescription = "Nested calls of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()` twice without restoring identity with the \
+ result of the first call.
+
+ Make sure to restore the identity after each clear identity call.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageNestedClearIdentityCallsPrimary(
+ firstCallVariableName: String,
+ secondCallVariableName: String
+ ): String = "The calling identity has already been cleared and returned into " +
+ "`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " +
+ "restoring the calling identity with " +
+ "`Binder.restoreCallingIdentity($firstCallVariableName)`."
+
+ fun getIncidentMessageNestedClearIdentityCallsSecondary(
+ firstCallVariableName: String
+ ): String = "Location of the `$firstCallVariableName` declaration."
+
+ /** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */
+ @JvmField
+ val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create(
+ id = "ClearIdentityCallNotFollowedByTryFinally",
+ briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " +
+ "statement",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but the next statement is not a `try` \
+ statement.
+
+ Use the following pattern for running operations with your own identity:
+
+ ```
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Code using your own identity
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ ```
+
+ Any calls/operations between `Binder.clearCallingIdentity()` and `try` \
+ statement risk throwing an exception without doing a safe and unconditional \
+ restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \
+ child of the `finally` block. If you do not follow the pattern, you may run \
+ code with your identity that was originally intended to run with the calling \
+ application's identity.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally(
+ variableName: String
+ ): String = "You cleared the calling identity and returned the result into " +
+ "`$variableName`, but the next statement is not a `try`-`finally` statement. " +
+ "Define a `try`-`finally` block after `$variableName` declaration to ensure a " +
+ "safe restore of the calling identity by calling " +
+ "`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " +
+ "of the `finally` block."
+
+ /** Issue: Binder.restoreCallingIdentity() is not in finally block */
+ @JvmField
+ val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create(
+ id = "RestoreIdentityCallNotInFinallyBlock",
+ briefDescription = "Binder.restoreCallingIdentity() is not in finally block",
+ explanation = """
+ You are restoring the original calling identity with \
+ `Binder.restoreCallingIdentity()`, but the call is not an immediate child of \
+ the `finally` block of the `try` statement.
+
+ Use the following pattern for running operations with your own identity:
+
+ ```
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Code using your own identity
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ ```
+
+ If you do not surround the code using your identity with the `try` statement \
+ and call `Binder.restoreCallingIdentity()` as an immediate child of the \
+ `finally` block, you may run code with your identity that was originally \
+ intended to run with the calling application's identity.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName: String): String =
+ "`Binder.restoreCallingIdentity($variableName)` is not an immediate child of the " +
+ "`finally` block of the try statement after `$variableName` declaration. " +
+ "Surround the call with `finally` block and call it unconditionally."
+
+ /** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create(
+ id = "UseOfCallerAwareMethodsWithClearedIdentity",
+ briefDescription = "Use of caller-aware methods after " +
+ "Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but used one of the methods below before \
+ restoring the identity. These methods will use your own identity instead of \
+ the caller's identity, so if this is expected replace them with methods that \
+ explicitly query your own identity such as `Process.myUid()`, \
+ `Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \
+ out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \
+ section.
+
+ ```
+ Binder.getCallingPid()
+ Binder.getCallingUid()
+ Binder.getCallingUidOrThrow()
+ Binder.getCallingUserHandle()
+ UserHandle.getCallingAppId()
+ UserHandle.getCallingUserId()
+ ```
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+ variableName: String,
+ methodName: String
+ ): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " +
+ "and returned into `$variableName`, so `$methodName` will be using your own " +
+ "identity instead of the caller's. Either explicitly query your own identity or " +
+ "move it after restoring the identity with " +
+ "`Binder.restoreCallingIdentity($variableName)`."
+ }
+}
diff --git a/tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt b/tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt
new file mode 100644
index 000000000000..0fd1a7621238
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt
@@ -0,0 +1,789 @@
+/*
+ * 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.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = CallingIdentityTokenDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ CallingIdentityTokenIssueRegistry.ISSUE_UNUSED_TOKEN,
+ CallingIdentityTokenIssueRegistry.ISSUE_NON_FINAL_TOKEN,
+ CallingIdentityTokenIssueRegistry.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ CallingIdentityTokenIssueRegistry.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ CallingIdentityTokenIssueRegistry
+ .ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ CallingIdentityTokenIssueRegistry.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+ )
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesInCorrectScenario() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethod() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ /** Unused token issue tests */
+
+ fun testDetectsUnusedTokens() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodImported() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodFullClass() {
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token1 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token1) in finally or \
+ remove token1. [UnusedTokenOfOriginalCallingIdentity]
+ final long token1 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: token2 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token2) in finally or \
+ remove token2. [UnusedTokenOfOriginalCallingIdentity]
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsUnusedTokensInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodTokenFromClearIdentity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodTokenNotFromClearIdentity() {
+ long token = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDoesNotDetectUsedTokensInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodTokenFromClearIdentity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodTokenNotFromClearIdentity() {
+ long token = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectsUnusedTokensWithSimilarNamesInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethod1() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethod2() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Non-final token issue tests */
+
+ fun testDetectsNonFinalTokens() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethod() {
+ long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token1 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token1. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token1 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:10: Warning: token2 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token2. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Nested clearCallingIdentity() calls issue tests */
+
+ fun testDetectsNestedClearCallingIdentityCalls() {
+ // Pattern: clear - clear - clear - restore - restore - restore
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethod() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ final long token3 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token3);
+ }
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:7: Warning: The calling identity has already \
+ been cleared and returned into token1. Move token2 declaration after \
+ restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+ [NestedClearCallingIdentityCalls]
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+ src/test/pkg/TestClass1.java:9: Warning: The calling identity has already \
+ been cleared and returned into token1. Move token3 declaration after \
+ restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+ [NestedClearCallingIdentityCalls]
+ final long token3 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** clearCallingIdentity() not followed by try-finally issue tests */
+
+ fun testDetectsClearIdentityCallNotFollowedByTryFinally() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodNoTry() {
+ final long token = Binder.clearCallingIdentity();
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodSomethingBetweenClearAndTry() {
+ final long token = Binder.clearCallingIdentity();
+ int pid = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodLocalVariableBetweenClearAndTry() {
+ final long token = Binder.clearCallingIdentity(), num = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodTryCatch() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodTryCatchInScopes() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ {
+ try {
+ } catch (Exception e) {
+ }
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:9: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity(), num = 0;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:24: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:31: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 5 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** restoreCallingIdentity() call not in finally block issue tests */
+
+ fun testDetectsRestoreCallingIdentityCallNotInFinally() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodImported() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ } finally {
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodFullClass() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ android.os.Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodRestoreInCatch() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ Binder.restoreCallingIdentity(token);
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:10: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:23: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsRestoreCallingIdentityCallNotInFinallyInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodOutsideFinally() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ } finally {
+ }
+ {
+ Binder.restoreCallingIdentity(token1);
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ {
+ {
+ {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ }
+ }
+ }
+ private void testMethodInsideFinallyInScopes() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ {
+ {
+ Binder.restoreCallingIdentity(token1);
+ }
+ }
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ {
+ {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ }
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:11: Warning: \
+ Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+ finally block of the try statement after token1 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token1);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:20: Warning: \
+ Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+ finally block of the try statement after token2 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token2);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:31: Warning: \
+ Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+ finally block of the try statement after token1 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token1);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:40: Warning: \
+ Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+ finally block of the try statement after token2 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token2);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 4 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Use of caller-aware methods after clearCallingIdentity() issue tests */
+
+ fun testDetectsUseOfCallerAwareMethodsWithClearedIdentityIssuesInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ import android.os.UserHandle;
+ public class TestClass1 {
+ private void testMethod() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int pid1 = Binder.getCallingPid();
+ int pid2 = android.os.Binder.getCallingPid();
+ int uid1 = Binder.getCallingUid();
+ int uid2 = android.os.Binder.getCallingUid();
+ int uid3 = Binder.getCallingUidOrThrow();
+ int uid4 = android.os.Binder.getCallingUidOrThrow();
+ UserHandle uh1 = Binder.getCallingUserHandle();
+ UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+ {
+ int appId1 = UserHandle.getCallingAppId();
+ int appId2 = android.os.UserHandle.getCallingAppId();
+ int userId1 = UserHandle.getCallingUserId();
+ int userId2 = android.os.UserHandle.getCallingUserId();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:8: Warning: You cleared the original identity \
+ with Binder.clearCallingIdentity() and returned into token, so \
+ Binder.getCallingPid() will be using your own identity instead of the \
+ caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int pid1 = Binder.getCallingPid();
+ ~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:9: Warning: You cleared the original identity \
+ with Binder.clearCallingIdentity() and returned into token, so \
+ android.os.Binder.getCallingPid() will be using your own identity instead \
+ of the caller's. Either explicitly query your own identity or move it \
+ after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int pid2 = android.os.Binder.getCallingPid();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:10: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ Binder.getCallingUid() will be using your own identity instead of the \
+ caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid1 = Binder.getCallingUid();
+ ~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ android.os.Binder.getCallingUid() will be using your own identity instead \
+ of the caller's. Either explicitly query your own identity or move it \
+ after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid2 = android.os.Binder.getCallingUid();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:12: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ Binder.getCallingUidOrThrow() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid3 = Binder.getCallingUidOrThrow();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:13: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ android.os.Binder.getCallingUidOrThrow() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid4 = android.os.Binder.getCallingUidOrThrow();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:14: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ Binder.getCallingUserHandle() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ UserHandle uh1 = Binder.getCallingUserHandle();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:15: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ android.os.Binder.getCallingUserHandle() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ UserHandle.getCallingAppId() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int appId1 = UserHandle.getCallingAppId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:18: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ android.os.UserHandle.getCallingAppId() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int appId2 = android.os.UserHandle.getCallingAppId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:19: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ UserHandle.getCallingUserId() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int userId1 = UserHandle.getCallingUserId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:20: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ android.os.UserHandle.getCallingUserId() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int userId2 = android.os.UserHandle.getCallingUserId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 12 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Stubs for classes used for testing */
+
+ private val binderStub: TestFile = java(
+ """
+ package android.os;
+ public class Binder {
+ public static final native long clearCallingIdentity() {
+ return 0;
+ }
+ public static final native void restoreCallingIdentity(long token) {
+ }
+ public static final native int getCallingPid() {
+ return 0;
+ }
+ public static final native int getCallingUid() {
+ return 0;
+ }
+ public static final int getCallingUidOrThrow() {
+ return 0;
+ }
+ public static final @NonNull UserHandle getCallingUserHandle() {
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+ }
+ }
+ """
+ ).indented()
+
+ private val userHandleStub: TestFile = java(
+ """
+ package android.os;
+ import android.annotation.AppIdInt;
+ import android.annotation.UserIdInt;
+ public class UserHandle {
+ public static @AppIdInt int getCallingAppId() {
+ return getAppId(Binder.getCallingUid());
+ }
+ public static @UserIdInt int getCallingUserId() {
+ return getUserId(Binder.getCallingUid());
+ }
+ public static @UserIdInt int getUserId(int uid) {
+ return 0;
+ }
+ public static @AppIdInt int getAppId(int uid) {
+ return 0;
+ }
+ public static UserHandle of(@UserIdInt int userId) {
+ return new UserHandle();
+ }
+ }
+ """
+ ).indented()
+
+ private val userIdIntStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface UserIdInt {
+ }
+ """
+ ).indented()
+
+ private val appIdIntStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface AppIdInt {
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(binderStub, userHandleStub, userIdIntStub, appIdIntStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}